diff --git a/src/Explorer/Controls/SmartUi/SmartUiComponent.tsx b/src/Explorer/Controls/SmartUi/SmartUiComponent.tsx index 7ce177d5a..4d3d8ac73 100644 --- a/src/Explorer/Controls/SmartUi/SmartUiComponent.tsx +++ b/src/Explorer/Controls/SmartUi/SmartUiComponent.tsx @@ -26,7 +26,7 @@ export type InputTypeValue = "number" | "string" | "boolean" | "object"; /* eslint-disable-next-line @typescript-eslint/no-explicit-any */ export type ChoiceItem = { label: string; key: string; value: any }; -export type InputType = Number | String | Boolean | ChoiceItem; +export type InputType = Number | String | Boolean | ChoiceItem | JSX.Element; export interface BaseInput { label: (() => Promise) | string; @@ -34,15 +34,15 @@ export interface BaseInput { type: InputTypeValue; onChange?: (currentState: Map, newValue: InputType) => Map; placeholder?: (() => Promise) | string; - customElement?: (() => Promise) | JSX.Element; + customElement?: ((currentValues: Map) => Promise) | JSX.Element; } /** * For now, this only supports integers */ export interface NumberInput extends BaseInput { - min?: (() => Promise) | number; - max?: (() => Promise) | number; + min: (() => Promise) | number; + max: (() => Promise) | number; step: (() => Promise) | number; defaultValue: (() => Promise) | number; inputType: "spin" | "slider"; @@ -94,9 +94,13 @@ export interface SmartUiComponentProps { interface SmartUiComponentState { currentValues: Map; errors: Map; + customInputIndex: number } export class SmartUiComponent extends React.Component { + private customInputs : AnyInput[] = [] + private shouldRenderCustomComponents = true + private static readonly labelStyle = { color: "#393939", fontFamily: "wf_segoe-ui_normal, 'Segoe UI', 'Segoe WP', Tahoma, Arial, sans-serif", @@ -107,12 +111,36 @@ export class SmartUiComponent extends React.Component => { + if (!this.customInputs.length) { + return + } + if (!this.shouldRenderCustomComponents) { + this.shouldRenderCustomComponents = true; + return; + } + + if (this.state.customInputIndex === this.customInputs.length) { + this.shouldRenderCustomComponents = false + this.setState({customInputIndex: 0}) + return + } + + const input = this.customInputs[this.state.customInputIndex] + const dataFieldName = input.dataFieldName; + const element = await (input.customElement as Function)(this.state.currentValues) + const { currentValues } = this.state; + currentValues.set(dataFieldName, element); + this.setState({ currentValues: currentValues, customInputIndex: this.state.customInputIndex + 1}); + } + private setDefaultValues = async (): Promise => { const defaults = new Map(); await this.setDefaults(this.props.descriptor.root, defaults); @@ -143,7 +171,7 @@ export class SmartUiComponent extends React.Component + } else { + return input.customElement as JSX.Element; + } + } + private renderInput(input: AnyInput): JSX.Element { if (input.customElement) { - return input.customElement as JSX.Element; + return this.renderCustomInput(input) } switch (input.type) { case "string": @@ -450,11 +488,18 @@ export class SmartUiComponent extends React.Component {this.renderNode(this.props.descriptor.root)} + await this.props.descriptor.onSubmit(this.state.currentValues)} /> + this.setDefaultValues()} + /> + ) : ( diff --git a/src/SelfServe/PropertyDescriptors.tsx b/src/SelfServe/PropertyDescriptors.tsx index 5eae84af7..2978ae59c 100644 --- a/src/SelfServe/PropertyDescriptors.tsx +++ b/src/SelfServe/PropertyDescriptors.tsx @@ -22,7 +22,7 @@ export const OnChange = ( return addToMap("onChange", onChange); }; -export const CustomElement = (customElement: (() => Promise) | JSX.Element): PropertyDecorator => { +export const CustomElement = (customElement: ((currentValues: Map) => Promise) | JSX.Element): PropertyDecorator => { return addToMap("customElement", customElement); }; diff --git a/src/SelfServe/SelfServe.tsx b/src/SelfServe/SelfServe.tsx index d40da2cb1..1877b1110 100644 --- a/src/SelfServe/SelfServe.tsx +++ b/src/SelfServe/SelfServe.tsx @@ -3,16 +3,28 @@ import { initializeIcons } from "office-ui-fabric-react/lib/Icons"; import React from "react"; import * as ReactDOM from "react-dom"; import { initializeConfiguration } from "../ConfigContext"; -import { SmartUiComponent } from "../Explorer/Controls/SmartUi/SmartUiComponent"; +import { Descriptor, SmartUiComponent } from "../Explorer/Controls/SmartUi/SmartUiComponent"; +import { getSelfServeType, SelfServeTypes } from "./SelfServeUtils"; import { SqlX } from "./SqlX/SqlX"; +const getDescriptor = (selfServeType : SelfServeTypes) : Descriptor => { + switch (selfServeType) { + case SelfServeTypes.sqlx: + return SqlX.toSmartUiDescriptor() + default: + return undefined; + } +} + const render = async () => { initializeIcons(); await initializeConfiguration(); + const selfServeType = getSelfServeType(window.location.search) + const smartUiDescriptor = getDescriptor(selfServeType) - const element = ( - - ); + const element = smartUiDescriptor ? + : +

Invalid self serve type!

ReactDOM.render(element, document.getElementById("selfServeContent")); }; diff --git a/src/SelfServe/SelfServeUtils.tsx b/src/SelfServe/SelfServeUtils.tsx index 40f3234bb..8cf105143 100644 --- a/src/SelfServe/SelfServeUtils.tsx +++ b/src/SelfServe/SelfServeUtils.tsx @@ -13,6 +13,8 @@ import { InputType } from "../Explorer/Controls/SmartUi/SmartUiComponent"; +const SelfServeType = "selfServeType" + export class SelfServeBase { public static toSmartUiDescriptor(): Descriptor { return Reflect.getMetadata(this.name, this) as Descriptor; @@ -38,7 +40,7 @@ export interface CommonInputTypes { inputType?: string; onChange?: (currentState: Map, newValue: InputType) => Map; onSubmit?: (currentValues: Map) => Promise; - customElement?: (() => Promise) | JSX.Element; + customElement?: ((currentValues: Map) => Promise) | JSX.Element; } const setValue = ( @@ -154,11 +156,13 @@ const addToDescriptor = ( const getChildFromRoot = (key: String, smartUiDescriptor: Descriptor): Node => { let i = 0; const children = smartUiDescriptor.root.children; - for (; i < children.length; i++) { - if (children[i].id === key) { + while (i < children.length) { + if (children[i]?.id === key) { const value = children[i]; delete children[i]; return value; + } else { + i++; } } return undefined; @@ -171,8 +175,8 @@ const getInput = (value: CommonInputTypes): AnyInput => { switch (value.type) { case "number": - if (!value.step || !value.defaultValue || !value.inputType) { - throw new Error("step, defaultValue and inputType are needed for number type"); + if (!value.step || !value.defaultValue || !value.inputType || !value.min || !value.max) { + throw new Error("step, min, miax, defaultValue and inputType are needed for number type"); } return value as NumberInput; case "string": @@ -189,3 +193,13 @@ const getInput = (value: CommonInputTypes): AnyInput => { return value as ChoiceInput; } }; + +export enum SelfServeTypes { + sqlx="sqlx" +} + +export const getSelfServeType = (search: string): SelfServeTypes => { + const params = new URLSearchParams(search); + const selfServeTypeParam = params.get(SelfServeType)?.toLowerCase() + return SelfServeTypes[selfServeTypeParam as keyof typeof SelfServeTypes] +} diff --git a/src/SelfServe/SqlX/SqlX.tsx b/src/SelfServe/SqlX/SqlX.tsx index d84b16cb0..3a722d008 100644 --- a/src/SelfServe/SqlX/SqlX.tsx +++ b/src/SelfServe/SqlX/SqlX.tsx @@ -23,7 +23,7 @@ import { instanceSizeOptions, onInstanceCountChange, onSubmit, - renderTextInput, + renderText, Sizes, sqlXInfo } from "./SqlXApis"; @@ -34,9 +34,10 @@ import { ChoiceItem } from "../../Explorer/Controls/SmartUi/SmartUiComponent"; @ClassInfo(getPromise(sqlXInfo)) @OnSubmit(onSubmit) export class SqlX extends SelfServeBase { - @Label(getPromise("About")) - @CustomElement(renderTextInput) - static about: string; + + @Label(getPromise("Description")) + @CustomElement(renderText("This is the description part of SqlX")) + static description: string; @PropertyInfo(getPromise(instanceSizeInfo)) @Label(getPromise("Instance Size")) @@ -44,15 +45,9 @@ export class SqlX extends SelfServeBase { @DefaultKey(getPromise(Sizes.OneCore4Gb)) static instanceSize: ChoiceItem; - @OnChange(onInstanceCountChange) - @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(getPromise("About")) + @CustomElement(renderText("This is the about part of SqlX")) + static about: string; @Label("Feature Allowed") @DefaultBooleanValue(false) @@ -63,4 +58,14 @@ export class SqlX extends SelfServeBase { @Label("Instance Name") @Placeholder("instance name") static instanceName: string; + + @OnChange(onInstanceCountChange) + @Label(getPromise("Instance Count")) + @Min(getPromise(0)) + @Max(getPromise(5)) + @Step(getPromise(1)) + @DefaultNumberValue(getPromise(1)) + @NumberInputType("slider") + @ParentOf(["instanceSize", "about", "instanceName", "isAllowed", ]) + static instanceCount: number; } diff --git a/src/SelfServe/SqlX/SqlXApis.tsx b/src/SelfServe/SqlX/SqlXApis.tsx index f2c625e7d..7743b5d2f 100644 --- a/src/SelfServe/SqlX/SqlXApis.tsx +++ b/src/SelfServe/SqlX/SqlXApis.tsx @@ -1,6 +1,7 @@ import { Text } from "office-ui-fabric-react"; import React from "react"; import { ChoiceItem, Info, InputType } from "../../Explorer/Controls/SmartUi/SmartUiComponent"; +import { TextComponent } from "./TextComponent"; export enum Sizes { OneCore4Gb = "OneCore4Gb", @@ -28,7 +29,7 @@ export const onInstanceCountChange = ( ): Map => { currentState.set("instanceCount", newValue); if ((newValue as number) === 1) { - currentState.set("isAllowed", false); + currentState.set("instanceSize", Sizes.OneCore4Gb); } return currentState; }; @@ -60,6 +61,10 @@ export const getPromise = => { - return SqlX is a new feature of Cosmos DB.; -}; +export const renderText = (text: string) : (currentValues: Map) => Promise => { + const f = async (currentValues: Map): Promise => { + //return SqlX is a new feature of Cosmos DB.; + return + }; + return f +} diff --git a/src/SelfServe/SqlX/TextComponent.tsx b/src/SelfServe/SqlX/TextComponent.tsx new file mode 100644 index 000000000..32a0f0f79 --- /dev/null +++ b/src/SelfServe/SqlX/TextComponent.tsx @@ -0,0 +1,15 @@ +import React from "react"; +import { Text } from "office-ui-fabric-react"; +import { InputType } from "../../Explorer/Controls/SmartUi/SmartUiComponent"; + +interface TextComponentProps { + text: string; + currentValues: Map +} + +export class TextComponent extends React.Component { + + public render() { + return {this.props.text}, instanceCount: {this.props.currentValues?.get("instanceCount")} + } +}