mirror of
https://github.com/Azure/cosmos-explorer.git
synced 2025-02-23 12:37:25 +00:00
added custom element and base class
This commit is contained in:
parent
2dbde9c31a
commit
90fb7e7d8f
@ -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;
|
||||
|
@ -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 <U extends SelfServeBaseCLass>(constructor: U) => {
|
||||
constructor;
|
||||
};
|
||||
}
|
||||
|
||||
export const SmartUi = (): ClassDecorator => {
|
||||
return (target: Function) => {
|
||||
toSmartUiDescriptor(target.name, target);
|
||||
|
@ -22,6 +22,10 @@ export const OnChange = (
|
||||
return addToMap("onChange", onChange);
|
||||
};
|
||||
|
||||
export const CustomElement = (customElement: (() => Promise<JSX.Element>) | JSX.Element): PropertyDecorator => {
|
||||
return addToMap("customElement", customElement);
|
||||
};
|
||||
|
||||
export const PropertyInfo = (info: (() => Promise<Info>) | Info): PropertyDecorator => {
|
||||
return addToMap("info", info);
|
||||
};
|
||||
|
@ -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<string, InputType>): Promise<void> => {
|
||||
console.log(currentValues.get("instanceCount"), currentValues.get("instanceSize"));
|
||||
};
|
||||
|
@ -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>) | Info;
|
||||
@ -32,6 +38,7 @@ export interface CommonInputTypes {
|
||||
inputType?: string;
|
||||
onChange?: (currentState: Map<string, InputType>, newValue: InputType) => Map<string, InputType>;
|
||||
onSubmit?: (currentValues: Map<string, InputType>) => Promise<void>;
|
||||
customElement?: (() => Promise<JSX.Element>) | JSX.Element;
|
||||
}
|
||||
|
||||
const setValue = <T extends keyof CommonInputTypes, K extends CommonInputTypes[T]>(
|
||||
|
@ -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 = <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",
|
||||
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<string, InputType>,
|
||||
newValue: InputType
|
||||
): Map<string, InputType> => {
|
||||
currentState.set("instanceCount", newValue);
|
||||
if ((newValue as number) === 1) {
|
||||
currentState.set("isAllowed", false);
|
||||
}
|
||||
return currentState;
|
||||
};
|
||||
|
||||
static onSubmit = async (currentValues: Map<string, InputType>): Promise<void> => {
|
||||
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) );
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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<string, InputType>,
|
||||
newValue: InputType
|
||||
): Map<string, InputType> => {
|
||||
currentState.set("instanceCount", newValue);
|
||||
if ((newValue as number) === 1) {
|
||||
currentState.set("isAllowed", false);
|
||||
}
|
||||
return currentState;
|
||||
};
|
||||
|
||||
export const onSubmit = async (currentValues: Map<string, InputType>): Promise<void> => {
|
||||
console.log(
|
||||
"instanceCount:" +
|
||||
currentValues.get("instanceCount") +
|
||||
", instanceSize:" +
|
||||
currentValues.get("instanceSize") +
|
||||
", instanceName:" +
|
||||
currentValues.get("instanceName") +
|
||||
", isAllowed:" +
|
||||
currentValues.get("isAllowed")
|
||||
);
|
||||
};
|
||||
|
||||
export const delay = (ms: number): Promise<void> => {
|
||||
return new Promise(resolve => setTimeout(resolve, ms));
|
||||
};
|
||||
|
||||
export const getPromise = <T extends number | string | boolean | ChoiceItem[] | Info>(value: T): (() => Promise<T>) => {
|
||||
const f = async (): Promise<T> => {
|
||||
console.log("delay start");
|
||||
await delay(100);
|
||||
console.log("delay end");
|
||||
return value;
|
||||
};
|
||||
return f;
|
||||
};
|
||||
|
||||
export const renderTextInput = async (): Promise<JSX.Element> => {
|
||||
return <Text>SqlX is a new feature of Cosmos DB</Text>;
|
||||
};
|
@ -4,7 +4,7 @@ import { SmartUiComponent, Descriptor, InputType } from "./SmartUiComponent";
|
||||
|
||||
describe("SmartUiComponent", () => {
|
||||
const exampleData: Descriptor = {
|
||||
onSubmit: async (currentValues: Map<string, InputType>) => {},
|
||||
onSubmit: async () => {},
|
||||
root: {
|
||||
id: "root",
|
||||
info: {
|
||||
|
@ -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>) | string;
|
||||
dataFieldName: string;
|
||||
type: InputTypeValue;
|
||||
onChange?: (currentState: Map<string, InputType>, newValue: InputType) => Map<string, InputType>;
|
||||
placeholder?: (() => Promise<string>) | string;
|
||||
customElement?: (() => Promise<JSX.Element>) | JSX.Element;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -41,9 +42,9 @@ interface BaseInput {
|
||||
*/
|
||||
export interface NumberInput extends BaseInput {
|
||||
min?: (() => Promise<number>) | number;
|
||||
max?: (() => Promise<number>) | number
|
||||
step: (() => Promise<number>) | number
|
||||
defaultValue: (() => Promise<number>) | number
|
||||
max?: (() => Promise<number>) | number;
|
||||
step: (() => Promise<number>) | number;
|
||||
defaultValue: (() => Promise<number>) | number;
|
||||
inputType: "spin" | "slider";
|
||||
}
|
||||
|
||||
@ -109,22 +110,22 @@ export class SmartUiComponent extends React.Component<SmartUiComponentProps, Sma
|
||||
errors: new Map()
|
||||
};
|
||||
|
||||
this.setDefaultValues()
|
||||
this.setDefaultValues();
|
||||
}
|
||||
|
||||
private setDefaultValues = async () : Promise<void> => {
|
||||
const defaults = new Map<string, InputType>()
|
||||
await this.setDefaults(this.props.descriptor.root, defaults)
|
||||
this.setState({currentValues: 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 = async (currentNode: Node, defaults: Map<string, InputType>) : Promise<void> => {
|
||||
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.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<SmartUiComponentProps, Sma
|
||||
};
|
||||
|
||||
private getModifiedInput = async (input: AnyInput): Promise<AnyInput> => {
|
||||
|
||||
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;
|
||||
}
|
||||
};
|
||||
|
||||
@ -391,7 +398,7 @@ export class SmartUiComponent extends React.Component<SmartUiComponentProps, Sma
|
||||
selectedKey={
|
||||
this.state.currentValues.has(dataFieldName)
|
||||
? (this.state.currentValues.get(dataFieldName) as string)
|
||||
: defaultKey as string
|
||||
: (defaultKey as string)
|
||||
}
|
||||
onChange={(_, item: IDropdownOption) => this.onInputChange(input, item.key.toString())}
|
||||
placeholder={placeholder as string}
|
||||
@ -411,6 +418,9 @@ export class SmartUiComponent extends React.Component<SmartUiComponentProps, Sma
|
||||
}
|
||||
|
||||
private renderInput(input: AnyInput): JSX.Element {
|
||||
if (input.customElement) {
|
||||
return input.customElement as JSX.Element;
|
||||
}
|
||||
switch (input.type) {
|
||||
case "string":
|
||||
return this.renderStringInput(input as StringInput);
|
||||
@ -437,8 +447,7 @@ export class SmartUiComponent extends React.Component<SmartUiComponentProps, Sma
|
||||
|
||||
render(): JSX.Element {
|
||||
const containerStackTokens: IStackTokens = { childrenGap: 20 };
|
||||
return (
|
||||
this.state.currentValues && this.state.currentValues.size ?
|
||||
return this.state.currentValues && this.state.currentValues.size ? (
|
||||
<Stack tokens={containerStackTokens}>
|
||||
{this.renderNode(this.props.descriptor.root)}
|
||||
<PrimaryButton
|
||||
@ -447,7 +456,7 @@ 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