From 90fb7e7d8f1b94e74c514ed52741cc3db31d5eac Mon Sep 17 00:00:00 2001 From: Srinath Narayanan Date: Mon, 7 Dec 2020 02:23:20 -0800 Subject: [PATCH] added custom element and base class --- .../Controls/Settings/SettingsComponent.tsx | 2 +- .../SelfServe/ClassDescriptors.tsx | 12 +- .../SelfServe/PropertyDescriptors.tsx | 4 + .../SelfServe/SelfServeComponent.tsx | 2 +- .../SelfServe/SelfServeUtils.tsx | 7 ++ .../SettingsSubComponents/SelfServe/SqlX.tsx | 110 +++++------------- .../SelfServe/SqlXApis.tsx | 65 +++++++++++ .../SmartUi/SmartUiComponent.test.tsx | 2 +- .../Controls/SmartUi/SmartUiComponent.tsx | 91 ++++++++------- 9 files changed, 161 insertions(+), 134 deletions(-) create mode 100644 src/Explorer/Controls/Settings/SettingsSubComponents/SelfServe/SqlXApis.tsx diff --git a/src/Explorer/Controls/Settings/SettingsComponent.tsx b/src/Explorer/Controls/Settings/SettingsComponent.tsx index 7356d720e..33c2db8a0 100644 --- a/src/Explorer/Controls/Settings/SettingsComponent.tsx +++ b/src/Explorer/Controls/Settings/SettingsComponent.tsx @@ -45,7 +45,7 @@ import { readMongoDBCollectionThroughRP } from "../../../Common/dataAccess/readM import { getIndexTransformationProgress } from "../../../Common/dataAccess/getIndexTransformationProgress"; import { getErrorMessage, getErrorStack } from "../../../Common/ErrorHandlingUtils"; import { isEmpty } from "underscore"; -import { SelfServeCmponent as SelfServeComponent } from "./SettingsSubComponents/SelfServe/SelfServeComponent"; +import { SelfServeComponent } from "./SettingsSubComponents/SelfServe/SelfServeComponent"; interface SettingsV2TabInfo { tab: SettingsV2TabTypes; diff --git a/src/Explorer/Controls/Settings/SettingsSubComponents/SelfServe/ClassDescriptors.tsx b/src/Explorer/Controls/Settings/SettingsSubComponents/SelfServe/ClassDescriptors.tsx index e5df4b07b..3bf9e466d 100644 --- a/src/Explorer/Controls/Settings/SettingsSubComponents/SelfServe/ClassDescriptors.tsx +++ b/src/Explorer/Controls/Settings/SettingsSubComponents/SelfServe/ClassDescriptors.tsx @@ -1,16 +1,6 @@ -import { Descriptor, Info, InputType } from "../../../SmartUi/SmartUiComponent"; +import { Info, InputType } from "../../../SmartUi/SmartUiComponent"; import { addPropertyToMap, toSmartUiDescriptor } from "./SelfServeUtils"; -interface SelfServeBaseCLass { - toSmartUiDescriptor: () => Descriptor; -} - -export function SelfServeClass() { - return (constructor: U) => { - constructor; - }; -} - export const SmartUi = (): ClassDecorator => { return (target: Function) => { toSmartUiDescriptor(target.name, target); diff --git a/src/Explorer/Controls/Settings/SettingsSubComponents/SelfServe/PropertyDescriptors.tsx b/src/Explorer/Controls/Settings/SettingsSubComponents/SelfServe/PropertyDescriptors.tsx index bf7a29b9a..a2919355c 100644 --- a/src/Explorer/Controls/Settings/SettingsSubComponents/SelfServe/PropertyDescriptors.tsx +++ b/src/Explorer/Controls/Settings/SettingsSubComponents/SelfServe/PropertyDescriptors.tsx @@ -22,6 +22,10 @@ export const OnChange = ( return addToMap("onChange", onChange); }; +export const CustomElement = (customElement: (() => Promise) | JSX.Element): PropertyDecorator => { + return addToMap("customElement", customElement); +}; + export const PropertyInfo = (info: (() => Promise) | Info): PropertyDecorator => { return addToMap("info", info); }; diff --git a/src/Explorer/Controls/Settings/SettingsSubComponents/SelfServe/SelfServeComponent.tsx b/src/Explorer/Controls/Settings/SettingsSubComponents/SelfServe/SelfServeComponent.tsx index c4205c211..7d624d075 100644 --- a/src/Explorer/Controls/Settings/SettingsSubComponents/SelfServe/SelfServeComponent.tsx +++ b/src/Explorer/Controls/Settings/SettingsSubComponents/SelfServe/SelfServeComponent.tsx @@ -2,7 +2,7 @@ import React from "react"; import { Descriptor, InputType, SmartUiComponent } from "../../../SmartUi/SmartUiComponent"; import { SqlX } from "./SqlX"; -export class SelfServeCmponent extends React.Component { +export class SelfServeComponent extends React.Component { private onSubmit = async (currentValues: Map): Promise => { console.log(currentValues.get("instanceCount"), currentValues.get("instanceSize")); }; diff --git a/src/Explorer/Controls/Settings/SettingsSubComponents/SelfServe/SelfServeUtils.tsx b/src/Explorer/Controls/Settings/SettingsSubComponents/SelfServe/SelfServeUtils.tsx index 010e39e31..3068810c0 100644 --- a/src/Explorer/Controls/Settings/SettingsSubComponents/SelfServe/SelfServeUtils.tsx +++ b/src/Explorer/Controls/Settings/SettingsSubComponents/SelfServe/SelfServeUtils.tsx @@ -13,6 +13,12 @@ import { InputType } from "../../../SmartUi/SmartUiComponent"; +export class SelfServeBase { + public static toSmartUiDescriptor(): Descriptor { + return Reflect.getMetadata(this.name, this) as Descriptor; + } +} + export interface CommonInputTypes { id: string; info?: (() => Promise) | Info; @@ -32,6 +38,7 @@ export interface CommonInputTypes { inputType?: string; onChange?: (currentState: Map, newValue: InputType) => Map; onSubmit?: (currentValues: Map) => Promise; + customElement?: (() => Promise) | JSX.Element; } const setValue = ( diff --git a/src/Explorer/Controls/Settings/SettingsSubComponents/SelfServe/SqlX.tsx b/src/Explorer/Controls/Settings/SettingsSubComponents/SelfServe/SqlX.tsx index e2389b229..0c6593bac 100644 --- a/src/Explorer/Controls/Settings/SettingsSubComponents/SelfServe/SqlX.tsx +++ b/src/Explorer/Controls/Settings/SettingsSubComponents/SelfServe/SqlX.tsx @@ -14,43 +14,39 @@ import { Placeholder, DefaultNumberValue, DefaultBooleanValue, - DefaultStringValue + DefaultStringValue, + CustomElement } from "./PropertyDescriptors"; -import { Descriptor, ChoiceItem, Info, InputType } from "../../../SmartUi/SmartUiComponent"; -import { SmartUi, ClassInfo, SelfServeClass, OnSubmit } from "./ClassDescriptors"; - -const getPromise = (value: T) : () => Promise => { - const f = async () : Promise => { - console.log("delay start") - await SqlX.delay(100) - console.log("delay end") - return value - } - return f -} -enum Sizes { - OneCore4Gb = "OneCore4Gb", - TwoCore8Gb = "TwoCore8Gb", - FourCore16Gb = "FourCore16Gb" -} +import { SmartUi, ClassInfo, OnSubmit } from "./ClassDescriptors"; +import { ChoiceItem } from "../../../SmartUi/SmartUiComponent"; +import { + getPromise, + instanceSizeInfo, + instanceSizeOptions, + onInstanceCountChange, + onSubmit, + renderTextInput, + Sizes, + sqlXInfo +} from "./SqlXApis"; +import { SelfServeBase } from "./SelfServeUtils"; @SmartUi() -@SelfServeClass() -@ClassInfo(getPromise(SqlX.sqlXInfo)) -@OnSubmit(SqlX.onSubmit) -export class SqlX { +@ClassInfo(getPromise(sqlXInfo)) +@OnSubmit(onSubmit) +export class SqlX extends SelfServeBase { + @Label(getPromise("About")) + @CustomElement(renderTextInput) + static about: string; - public static toSmartUiDescriptor = (): Descriptor => { - return Reflect.getMetadata(SqlX.name, SqlX) as Descriptor; - }; - - @PropertyInfo(getPromise(SqlX.instanceSizeInfo)) + @PropertyInfo(getPromise(instanceSizeInfo)) @Label(getPromise("Instance Size")) - @Choices(getPromise(SqlX.instanceSizeOptions)) + @Choices(getPromise(instanceSizeOptions)) + //@Choices(instanceSizeOptions) @DefaultKey(getPromise(Sizes.OneCore4Gb)) static instanceSize: ChoiceItem; - @OnChange(SqlX.onInstanceCountChange) + @OnChange(onInstanceCountChange) @Label(getPromise("Instance Count")) @Min(getPromise(0)) @Max(getPromise(5)) @@ -60,57 +56,13 @@ export class SqlX { @ParentOf(["instanceSize", "instanceName", "isAllowed"]) static instanceCount: number; - @Label(getPromise("Feature Allowed")) - @DefaultBooleanValue(getPromise(false)) - @TrueLabel(getPromise("allowed")) - @FalseLabel(getPromise("not allowed")) + @Label("Feature Allowed") + @DefaultBooleanValue(false) + @TrueLabel("allowed") + @FalseLabel("not allowed") static isAllowed: boolean; - @Label(getPromise("Instance Name")) - @DefaultStringValue(getPromise("asdf")) - @Placeholder(getPromise("instance name")) + @Label("Instance Name") + @Placeholder("instance name") static instanceName: string; - - 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 } - ]; - - static sqlXInfo: Info = { - message: "SqlX is a self serve class" - }; - - static instanceSizeInfo: Info = { - message: "instance size will be updated in the future" - }; - - static onInstanceCountChange = ( - currentState: Map, - newValue: InputType - ): Map => { - currentState.set("instanceCount", newValue); - if ((newValue as number) === 1) { - currentState.set("isAllowed", false); - } - return currentState; - }; - - static onSubmit = async (currentValues: Map): Promise => { - console.log( - "instanceCount:" + - currentValues.get("instanceCount") + - ", instanceSize:" + - currentValues.get("instanceSize") + - ", instanceName:" + - currentValues.get("instanceName") + - ", isAllowed:" + - currentValues.get("isAllowed") - ); - }; - - static delay = (ms: number) => { - return new Promise( resolve => setTimeout(resolve, ms) ); - } } - diff --git a/src/Explorer/Controls/Settings/SettingsSubComponents/SelfServe/SqlXApis.tsx b/src/Explorer/Controls/Settings/SettingsSubComponents/SelfServe/SqlXApis.tsx new file mode 100644 index 000000000..5d1977f24 --- /dev/null +++ b/src/Explorer/Controls/Settings/SettingsSubComponents/SelfServe/SqlXApis.tsx @@ -0,0 +1,65 @@ +import { Text } from "office-ui-fabric-react"; +import React from "react"; +import { ChoiceItem, Info, InputType } from "../../../SmartUi/SmartUiComponent"; + +export enum Sizes { + OneCore4Gb = "OneCore4Gb", + TwoCore8Gb = "TwoCore8Gb", + FourCore16Gb = "FourCore16Gb" +} + +export const 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 } +]; + +export const sqlXInfo: Info = { + message: "SqlX is a self serve class" +}; + +export const instanceSizeInfo: Info = { + message: "instance size will be updated in the future" +}; + +export const onInstanceCountChange = ( + currentState: Map, + newValue: InputType +): Map => { + currentState.set("instanceCount", newValue); + if ((newValue as number) === 1) { + currentState.set("isAllowed", false); + } + return currentState; +}; + +export const onSubmit = async (currentValues: Map): Promise => { + console.log( + "instanceCount:" + + currentValues.get("instanceCount") + + ", instanceSize:" + + currentValues.get("instanceSize") + + ", instanceName:" + + currentValues.get("instanceName") + + ", isAllowed:" + + currentValues.get("isAllowed") + ); +}; + +export const delay = (ms: number): Promise => { + return new Promise(resolve => setTimeout(resolve, ms)); +}; + +export const getPromise = (value: T): (() => Promise) => { + const f = async (): Promise => { + console.log("delay start"); + await delay(100); + console.log("delay end"); + return value; + }; + return f; +}; + +export const renderTextInput = async (): Promise => { + return SqlX is a new feature of Cosmos DB; +}; diff --git a/src/Explorer/Controls/SmartUi/SmartUiComponent.test.tsx b/src/Explorer/Controls/SmartUi/SmartUiComponent.test.tsx index 71c2df5d6..1ae2b0896 100644 --- a/src/Explorer/Controls/SmartUi/SmartUiComponent.test.tsx +++ b/src/Explorer/Controls/SmartUi/SmartUiComponent.test.tsx @@ -4,7 +4,7 @@ import { SmartUiComponent, Descriptor, InputType } from "./SmartUiComponent"; describe("SmartUiComponent", () => { const exampleData: Descriptor = { - onSubmit: async (currentValues: Map) => {}, + onSubmit: async () => {}, root: { id: "root", info: { diff --git a/src/Explorer/Controls/SmartUi/SmartUiComponent.tsx b/src/Explorer/Controls/SmartUi/SmartUiComponent.tsx index b00fd6599..7ce177d5a 100644 --- a/src/Explorer/Controls/SmartUi/SmartUiComponent.tsx +++ b/src/Explorer/Controls/SmartUi/SmartUiComponent.tsx @@ -28,12 +28,13 @@ export type ChoiceItem = { label: string; key: string; value: any }; export type InputType = Number | String | Boolean | ChoiceItem; -interface BaseInput { +export interface BaseInput { label: (() => Promise) | string; dataFieldName: string; type: InputTypeValue; onChange?: (currentState: Map, newValue: InputType) => Map; placeholder?: (() => Promise) | string; + customElement?: (() => Promise) | JSX.Element; } /** @@ -41,9 +42,9 @@ interface BaseInput { */ export interface NumberInput extends BaseInput { min?: (() => Promise) | number; - max?: (() => Promise) | number - step: (() => Promise) | number - defaultValue: (() => Promise) | number + max?: (() => Promise) | number; + step: (() => Promise) | number; + defaultValue: (() => Promise) | number; inputType: "spin" | "slider"; } @@ -109,22 +110,22 @@ export class SmartUiComponent extends React.Component => { - const defaults = new Map() - await this.setDefaults(this.props.descriptor.root, defaults) - this.setState({currentValues: defaults}) - } + private setDefaultValues = async (): Promise => { + const defaults = new Map(); + await this.setDefaults(this.props.descriptor.root, defaults); + this.setState({ currentValues: defaults }); + }; - private setDefaults = async (currentNode: Node, defaults: Map) : Promise => { + private setDefaults = async (currentNode: Node, defaults: Map): Promise => { if (currentNode.info && currentNode.info instanceof Function) { - currentNode.info = await (currentNode.info as Function)() + currentNode.info = await (currentNode.info as Function)(); } if (currentNode.input) { - currentNode.input = await this.getModifiedInput(currentNode.input) + currentNode.input = await this.getModifiedInput(currentNode.input); defaults.set(currentNode.input.dataFieldName, this.getDefaultValue(currentNode.input)); } @@ -132,58 +133,64 @@ export class SmartUiComponent extends React.Component => { - if (input.label instanceof Function) { - input.label = await (input.label as Function)() + input.label = await (input.label as Function)(); } if (input.placeholder instanceof Function) { - input.placeholder = await (input.placeholder as Function)() + input.placeholder = await (input.placeholder as Function)(); + } + + if (input.customElement) { + if (input.customElement instanceof Function) { + input.customElement = await (input.customElement as Function)(); + } + return input; } switch (input.type) { case "string": - const stringInput = input as StringInput + const stringInput = input as StringInput; if (stringInput.defaultValue instanceof Function) { - stringInput.defaultValue = await (stringInput.defaultValue as Function)() + stringInput.defaultValue = await (stringInput.defaultValue as Function)(); } return stringInput; case "number": - const numberInput = input as NumberInput + const numberInput = input as NumberInput; if (numberInput.defaultValue instanceof Function) { - numberInput.defaultValue = await (numberInput.defaultValue as Function)() + numberInput.defaultValue = await (numberInput.defaultValue as Function)(); } if (numberInput.min instanceof Function) { - numberInput.min = await (numberInput.min as Function)() + numberInput.min = await (numberInput.min as Function)(); } if (numberInput.max instanceof Function) { - numberInput.max = await (numberInput.max as Function)() + numberInput.max = await (numberInput.max as Function)(); } if (numberInput.step instanceof Function) { - numberInput.step = await (numberInput.step as Function)() + numberInput.step = await (numberInput.step as Function)(); } return numberInput; case "boolean": - const booleanInput = input as BooleanInput + const booleanInput = input as BooleanInput; if (booleanInput.defaultValue instanceof Function) { - booleanInput.defaultValue = await (booleanInput.defaultValue as Function)() + booleanInput.defaultValue = await (booleanInput.defaultValue as Function)(); } if (booleanInput.trueLabel instanceof Function) { - booleanInput.trueLabel = await (booleanInput.trueLabel as Function)() + booleanInput.trueLabel = await (booleanInput.trueLabel as Function)(); } if (booleanInput.falseLabel instanceof Function) { - booleanInput.falseLabel = await (booleanInput.falseLabel as Function)() + booleanInput.falseLabel = await (booleanInput.falseLabel as Function)(); } return booleanInput; default: - const enumInput = input as ChoiceInput + const enumInput = input as ChoiceInput; if (enumInput.defaultKey instanceof Function) { - enumInput.defaultKey = await (enumInput.defaultKey as Function)() + enumInput.defaultKey = await (enumInput.defaultKey as Function)(); } if (enumInput.choices instanceof Function) { - enumInput.choices = await (enumInput.choices as Function)() + enumInput.choices = await (enumInput.choices as Function)(); } - return enumInput + return enumInput; } }; @@ -297,12 +304,12 @@ export class SmartUiComponent extends React.Component this.onInputChange(input, item.key.toString())} placeholder={placeholder as string} @@ -411,6 +418,9 @@ export class SmartUiComponent extends React.Component {this.renderNode(this.props.descriptor.root)} await this.props.descriptor.onSubmit(this.state.currentValues)} /> - : + ) : ( ); }