Removed reactbinding changes

This commit is contained in:
Srinath Narayanan
2021-01-15 07:09:14 -08:00
parent 41f37055ef
commit 3fb4af53c8
11 changed files with 68 additions and 73 deletions

View File

@@ -15,7 +15,7 @@ import * as ReactDOM from "react-dom";
export interface ReactAdapter { export interface ReactAdapter {
parameters: any; parameters: any;
renderComponent: (() => Promise<JSX.Element>) | (() => JSX.Element); renderComponent: () => JSX.Element;
setElement?: (elt: Element) => void; setElement?: (elt: Element) => void;
} }
@@ -36,12 +36,12 @@ export class Registerer {
} }
// If any of the ko observable change inside parameters, trigger a new render. // If any of the ko observable change inside parameters, trigger a new render.
ko.computed(() => ko.toJSON(adapter.parameters)).subscribe(async () => ko.computed(() => ko.toJSON(adapter.parameters)).subscribe(() =>
ReactDOM.render(await adapter.renderComponent(), element) ReactDOM.render(adapter.renderComponent(), element)
); );
// Initial rendering at mount point // Initial rendering at mount point
Promise.resolve(adapter.renderComponent()).then(component => ReactDOM.render(component, element)); ReactDOM.render(adapter.renderComponent(), element);
} }
} as ko.BindingHandler; } as ko.BindingHandler;
} }

View File

@@ -126,7 +126,7 @@ export class Features {
public static readonly enableSchema = "enableschema"; public static readonly enableSchema = "enableschema";
public static readonly enableSDKoperations = "enablesdkoperations"; public static readonly enableSDKoperations = "enablesdkoperations";
public static readonly showMinRUSurvey = "showminrusurvey"; public static readonly showMinRUSurvey = "showminrusurvey";
public static readonly selfServeTypeForTest = "selfservetypefortest"; public static readonly selfServeType = "selfservetype";
} }
// flight names returned from the portal are always lowercase // flight names returned from the portal are always lowercase

View File

@@ -15,7 +15,7 @@ import DocumentId from "../Explorer/Tree/DocumentId";
import StoredProcedure from "../Explorer/Tree/StoredProcedure"; import StoredProcedure from "../Explorer/Tree/StoredProcedure";
import Trigger from "../Explorer/Tree/Trigger"; import Trigger from "../Explorer/Tree/Trigger";
import UserDefinedFunction from "../Explorer/Tree/UserDefinedFunction"; import UserDefinedFunction from "../Explorer/Tree/UserDefinedFunction";
import { SelfServeTypes } from "../SelfServe/SelfServeUtils"; import { SelfServeType } from "../SelfServe/SelfServeUtils";
import { UploadDetails } from "../workers/upload/definitions"; import { UploadDetails } from "../workers/upload/definitions";
import * as DataModels from "./DataModels"; import * as DataModels from "./DataModels";
import { SubscriptionType } from "./SubscriptionType"; import { SubscriptionType } from "./SubscriptionType";
@@ -396,7 +396,7 @@ export interface DataExplorerInputsFrame {
isAuthWithresourceToken?: boolean; isAuthWithresourceToken?: boolean;
defaultCollectionThroughput?: CollectionCreationDefaults; defaultCollectionThroughput?: CollectionCreationDefaults;
flights?: readonly string[]; flights?: readonly string[];
selfServeType?: SelfServeTypes; selfServeType?: SelfServeType;
} }
export interface CollectionCreationDefaults { export interface CollectionCreationDefaults {

View File

@@ -48,7 +48,7 @@ export const FeaturePanelComponent: React.FunctionComponent = () => {
{ key: "feature.hosteddataexplorerenabled", label: "Hosted Data Explorer (deprecated?)", value: "true" }, { key: "feature.hosteddataexplorerenabled", label: "Hosted Data Explorer (deprecated?)", value: "true" },
{ key: "feature.enablettl", label: "Enable TTL", value: "true" }, { key: "feature.enablettl", label: "Enable TTL", value: "true" },
{ key: "feature.enablegallerypublish", label: "Enable Notebook Gallery Publishing", value: "true" }, { key: "feature.enablegallerypublish", label: "Enable Notebook Gallery Publishing", value: "true" },
{ key: "feature.selfServeTypeForTest", label: "Self serve type passed on for testing", value: "sample" }, { key: "feature.selfServeType", label: "Self serve feature", value: "sample" },
{ {
key: "feature.enableLinkInjection", key: "feature.enableLinkInjection",
label: "Enable Injecting Notebook Viewer Link into the first cell", label: "Enable Injecting Notebook Viewer Link into the first cell",

View File

@@ -5,7 +5,6 @@ import { SpinButton } from "office-ui-fabric-react/lib/SpinButton";
import { Dropdown, IDropdownOption } from "office-ui-fabric-react/lib/Dropdown"; import { Dropdown, IDropdownOption } from "office-ui-fabric-react/lib/Dropdown";
import { TextField } from "office-ui-fabric-react/lib/TextField"; import { TextField } from "office-ui-fabric-react/lib/TextField";
import { Text } from "office-ui-fabric-react/lib/Text"; import { Text } from "office-ui-fabric-react/lib/Text";
import { InputType } from "../../Tables/Constants";
import { RadioSwitchComponent } from "../RadioSwitchComponent/RadioSwitchComponent"; import { RadioSwitchComponent } from "../RadioSwitchComponent/RadioSwitchComponent";
import { Stack, IStackTokens } from "office-ui-fabric-react/lib/Stack"; import { Stack, IStackTokens } from "office-ui-fabric-react/lib/Stack";
import { Link, MessageBar, MessageBarType, PrimaryButton, Spinner, SpinnerSize } from "office-ui-fabric-react"; import { Link, MessageBar, MessageBarType, PrimaryButton, Spinner, SpinnerSize } from "office-ui-fabric-react";
@@ -35,7 +34,7 @@ type infoPromise = () => Promise<Info>;
/* eslint-disable-next-line @typescript-eslint/no-explicit-any */ /* eslint-disable-next-line @typescript-eslint/no-explicit-any */
export type DropdownItem = { label: string; key: string }; export type DropdownItem = { label: string; key: string };
export type InputType = number | string | boolean | DropdownItem | JSX.Element; export type InputType = number | string | boolean | DropdownItem;
export interface BaseInput { export interface BaseInput {
label: (() => Promise<string>) | string; label: (() => Promise<string>) | string;
@@ -117,7 +116,7 @@ export class SmartUiComponent extends React.Component<SmartUiComponentProps, Sma
}; };
componentDidMount(): void { componentDidMount(): void {
this.setDefaultValues(); this.initializeSmartUiComponent();
} }
constructor(props: SmartUiComponentProps) { constructor(props: SmartUiComponentProps) {
@@ -130,23 +129,25 @@ export class SmartUiComponent extends React.Component<SmartUiComponentProps, Sma
}; };
} }
private setDefaultValues = async (): Promise<void> => { private initializeSmartUiComponent = async (): Promise<void> => {
this.setState({ isRefreshing: true }); this.setState({ isRefreshing: true });
await this.setDefaults(this.props.descriptor.root); await this.initializeNode(this.props.descriptor.root);
await this.setDefaults();
this.setState({ isRefreshing: false }); this.setState({ isRefreshing: false });
await this.initialize();
}; };
private initialize = async (): Promise<void> => { private setDefaults = async (): Promise<void> => {
this.setState({ isRefreshing: true }); this.setState({ isRefreshing: true });
let { currentValues, baselineValues } = this.state; let { currentValues, baselineValues } = this.state;
const initialValues = await this.props.descriptor.initialize(); const initialValues = await this.props.descriptor.initialize();
for (const key of initialValues.keys()) { for (const key of initialValues.keys()) {
if (this.props.descriptor.inputNames.indexOf(key) === -1) { if (this.props.descriptor.inputNames.indexOf(key) === -1) {
console.log(this.props.descriptor.inputNames);
this.setState({ isRefreshing: false }); this.setState({ isRefreshing: false });
throw new Error(`${key} is not an input property of this class.`); throw new Error(`${key} is not an input property of this class.`);
} }
currentValues = currentValues.set(key, initialValues.get(key)); currentValues = currentValues.set(key, initialValues.get(key));
baselineValues = baselineValues.set(key, initialValues.get(key)); baselineValues = baselineValues.set(key, initialValues.get(key));
} }
@@ -162,7 +163,7 @@ export class SmartUiComponent extends React.Component<SmartUiComponentProps, Sma
this.setState({ currentValues: currentValues }); this.setState({ currentValues: currentValues });
}; };
private setDefaults = async (currentNode: Node): Promise<void> => { private initializeNode = async (currentNode: Node): Promise<void> => {
if (currentNode.info && currentNode.info instanceof Function) { if (currentNode.info && currentNode.info instanceof Function) {
currentNode.info = await (currentNode.info as infoPromise)(); currentNode.info = await (currentNode.info as infoPromise)();
} }
@@ -170,7 +171,7 @@ export class SmartUiComponent extends React.Component<SmartUiComponentProps, Sma
if (currentNode.input) { if (currentNode.input) {
currentNode.input = await this.getModifiedInput(currentNode.input); currentNode.input = await this.getModifiedInput(currentNode.input);
} }
const promises = currentNode.children?.map(async (child: Node) => await this.setDefaults(child)); const promises = currentNode.children?.map(async (child: Node) => await this.initializeNode(child));
if (promises) { if (promises) {
await Promise.all(promises); await Promise.all(promises);
} }
@@ -222,24 +223,6 @@ export class SmartUiComponent extends React.Component<SmartUiComponentProps, Sma
} }
}; };
private getDefaultValue = (input: AnyInput): InputType => {
switch (input.type) {
case "string": {
const stringInput = input as StringInput;
return stringInput.defaultValue ? (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 DropdownInput).defaultKey as string;
}
}
};
private renderInfo(info: Info): JSX.Element { private renderInfo(info: Info): JSX.Element {
return ( return (
<MessageBar> <MessageBar>
@@ -265,7 +248,7 @@ export class SmartUiComponent extends React.Component<SmartUiComponentProps, Sma
} }
}; };
private renderStringInput(input: StringInput): JSX.Element { private renderTextInput(input: StringInput): JSX.Element {
const value = this.state.currentValues.get(input.dataFieldName) as string; const value = this.state.currentValues.get(input.dataFieldName) as string;
return ( return (
<div className="stringInputContainer"> <div className="stringInputContainer">
@@ -423,7 +406,7 @@ export class SmartUiComponent extends React.Component<SmartUiComponentProps, Sma
); );
} }
private renderEnumInput(input: DropdownInput): JSX.Element { private renderDropdownInput(input: DropdownInput): JSX.Element {
const { label, defaultKey: defaultKey, dataFieldName, choices, placeholder } = input; const { label, defaultKey: defaultKey, dataFieldName, choices, placeholder } = input;
return ( return (
<Dropdown <Dropdown
@@ -460,13 +443,15 @@ export class SmartUiComponent extends React.Component<SmartUiComponentProps, Sma
} }
switch (input.type) { switch (input.type) {
case "string": case "string":
return this.renderStringInput(input as StringInput); return this.renderTextInput(input as StringInput);
case "number": case "number":
return this.renderNumberInput(input as NumberInput); return this.renderNumberInput(input as NumberInput);
case "boolean": case "boolean":
return this.renderBooleanInput(input as BooleanInput); return this.renderBooleanInput(input as BooleanInput);
case "object":
return this.renderDropdownInput(input as DropdownInput);
default: default:
return this.renderEnumInput(input as DropdownInput); throw new Error(`Unknown input type: ${input.type}`);
} }
} }
@@ -496,7 +481,7 @@ export class SmartUiComponent extends React.Component<SmartUiComponentProps, Sma
text="submit" text="submit"
onClick={async () => { onClick={async () => {
await this.props.descriptor.onSubmit(this.state.currentValues); await this.props.descriptor.onSubmit(this.state.currentValues);
this.initialize(); this.setDefaults();
}} }}
/> />
<PrimaryButton styles={{ root: { width: 100 } }} text="discard" onClick={() => this.discard()} /> <PrimaryButton styles={{ root: { width: 100 } }} text="discard" onClick={() => this.discard()} />

View File

@@ -89,7 +89,7 @@ import { IChoiceGroupProps } from "office-ui-fabric-react";
import { getErrorMessage, handleError, getErrorStack } from "../Common/ErrorHandlingUtils"; import { getErrorMessage, handleError, getErrorStack } from "../Common/ErrorHandlingUtils";
import { SubscriptionType } from "../Contracts/SubscriptionType"; import { SubscriptionType } from "../Contracts/SubscriptionType";
import { SelfServeLoadingComponentAdapter } from "../SelfServe/SelfServeLoadingComponentAdapter"; import { SelfServeLoadingComponentAdapter } from "../SelfServe/SelfServeLoadingComponentAdapter";
import { SelfServeTypes } from "../SelfServe/SelfServeUtils"; import { SelfServeType } from "../SelfServe/SelfServeUtils";
import { SelfServeComponentAdapter } from "../SelfServe/SelfServeComponentAdapter"; import { SelfServeComponentAdapter } from "../SelfServe/SelfServeComponentAdapter";
BindingHandlersRegisterer.registerBindingHandlers(); BindingHandlersRegisterer.registerBindingHandlers();
@@ -134,7 +134,7 @@ export default class Explorer {
public isEnableMongoCapabilityPresent: ko.Computed<boolean>; public isEnableMongoCapabilityPresent: ko.Computed<boolean>;
public isServerlessEnabled: ko.Computed<boolean>; public isServerlessEnabled: ko.Computed<boolean>;
public isAccountReady: ko.Observable<boolean>; public isAccountReady: ko.Observable<boolean>;
public selfServeType: ko.Observable<SelfServeTypes>; public selfServeType: ko.Observable<SelfServeType>;
public canSaveQueries: ko.Computed<boolean>; public canSaveQueries: ko.Computed<boolean>;
public features: ko.Observable<any>; public features: ko.Observable<any>;
public serverId: ko.Observable<string>; public serverId: ko.Observable<string>;
@@ -299,7 +299,7 @@ export default class Explorer {
} }
}); });
this.isAccountReady = ko.observable<boolean>(false); this.isAccountReady = ko.observable<boolean>(false);
this.selfServeType = ko.observable<SelfServeTypes>(undefined); this.selfServeType = ko.observable<SelfServeType>(undefined);
this._isInitializingNotebooks = false; this._isInitializingNotebooks = false;
this._isInitializingSparkConnectionInfo = false; this._isInitializingSparkConnectionInfo = false;
this.arcadiaToken = ko.observable<string>(); this.arcadiaToken = ko.observable<string>();
@@ -1851,14 +1851,16 @@ export default class Explorer {
} }
public setSelfServeType(inputs: ViewModels.DataExplorerInputsFrame): void { public setSelfServeType(inputs: ViewModels.DataExplorerInputsFrame): void {
const selfServeTypeForTest = inputs.features[Constants.Features.selfServeTypeForTest]; const selfServeFeature = inputs.features[Constants.Features.selfServeType];
if (selfServeTypeForTest) { if (selfServeFeature) {
const selfServeType = SelfServeTypes[selfServeTypeForTest?.toLowerCase() as keyof typeof SelfServeTypes]; // self serve type received from query string
this.selfServeType(selfServeType ? selfServeType : SelfServeTypes.invalid); const selfServeType = SelfServeType[selfServeFeature?.toLowerCase() as keyof typeof SelfServeType];
this.selfServeType(selfServeType ? selfServeType : SelfServeType.invalid);
} else if (inputs.selfServeType) { } else if (inputs.selfServeType) {
// self serve type received from portal
this.selfServeType(inputs.selfServeType); this.selfServeType(inputs.selfServeType);
} else { } else {
this.selfServeType(SelfServeTypes.none); this.selfServeType(SelfServeType.none);
} }
} }

View File

@@ -79,6 +79,7 @@ import refreshImg from "../images/refresh-cosmos.svg";
import arrowLeftImg from "../images/imgarrowlefticon.svg"; import arrowLeftImg from "../images/imgarrowlefticon.svg";
import { KOCommentEnd, KOCommentIfStart } from "./koComment"; import { KOCommentEnd, KOCommentIfStart } from "./koComment";
import { Spinner, SpinnerSize } from "office-ui-fabric-react"; import { Spinner, SpinnerSize } from "office-ui-fabric-react";
import { SelfServeType } from "./SelfServe/SelfServeUtils";
// TODO: Encapsulate and reuse all global variables as environment variables // TODO: Encapsulate and reuse all global variables as environment variables
window.authType = AuthType.AAD; window.authType = AuthType.AAD;
@@ -382,7 +383,6 @@ const App: React.FunctionComponent = () => {
</div> </div>
{/* Explorer Connection - Encryption Token / AAD - End */} {/* Explorer Connection - Encryption Token / AAD - End */}
{/* Global loader - Start */} {/* Global loader - Start */}
{window.dataExplorer && <Spinner size={SpinnerSize.large} />}
<div className="splashLoaderContainer" data-bind="visible: isRefreshingExplorer"> <div className="splashLoaderContainer" data-bind="visible: isRefreshingExplorer">
<div className="splashLoaderContentContainer"> <div className="splashLoaderContentContainer">
@@ -391,8 +391,10 @@ const App: React.FunctionComponent = () => {
<p className="connectExplorerContent"> <p className="connectExplorerContent">
<img src={hdeConnectImage} alt="Azure Cosmos DB" /> <img src={hdeConnectImage} alt="Azure Cosmos DB" />
</p> </p>
<p className="splashLoaderTitle">Welcome to Azure Cosmos DB</p> <p className="splashLoaderTitle" id="explorerLoadingStatusTitle">
<p className="splashLoaderText" role="alert"> Welcome to Azure Cosmos DB
</p>
<p className="splashLoaderText" id="explorerLoadingStatusText" role="alert">
Connecting... Connecting...
</p> </p>
</div> </div>

View File

@@ -49,13 +49,13 @@ const initializeMaxThroughput = async (): Promise<number> => {
- Needs to define an onSubmit() function, a callback for when the submit button is clicked. - Needs to define an onSubmit() function, a callback for when the submit button is clicked.
- Needs to define an initialize() function, to set default values for the inputs. - Needs to define an initialize() function, to set default values for the inputs.
You can test this self serve UI by using the featureflag '?feature.selfServeTypeForTest=example' You can test this self serve UI by using the featureflag '?feature.selfServeType=example'
and plumb in similar feature flags for your own self serve class. and plumb in similar feature flags for your own self serve class.
*/ */
/* /*
@IsDisplayable() @IsDisplayable()
- role: Generated the JSON required to convert this class into the required UI. This is done during compile time. - role: Indicates to the compiler that UI should be generated from this class.
*/ */
@IsDisplayable() @IsDisplayable()
/* /*

View File

@@ -8,23 +8,23 @@ import * as React from "react";
import { ReactAdapter } from "../Bindings/ReactBindingHandler"; import { ReactAdapter } from "../Bindings/ReactBindingHandler";
import Explorer from "../Explorer/Explorer"; import Explorer from "../Explorer/Explorer";
import { Descriptor, SmartUiComponent } from "../Explorer/Controls/SmartUi/SmartUiComponent"; import { Descriptor, SmartUiComponent } from "../Explorer/Controls/SmartUi/SmartUiComponent";
import { SelfServeTypes } from "./SelfServeUtils"; import { SelfServeType } from "./SelfServeUtils";
export class SelfServeComponentAdapter implements ReactAdapter { export class SelfServeComponentAdapter implements ReactAdapter {
public parameters: ko.Observable<number>; public parameters: ko.Observable<Descriptor>;
public container: Explorer; public container: Explorer;
constructor(container: Explorer) { constructor(container: Explorer) {
this.container = container; this.container = container;
this.parameters = ko.observable(Date.now()); this.parameters = ko.observable(undefined)
this.container.selfServeType.subscribe(() => { this.container.selfServeType.subscribe(() => {
this.triggerRender(); this.triggerRender();
}); });
} }
public static getDescriptor = async (selfServeType: SelfServeTypes): Promise<Descriptor> => { public static getDescriptor = async (selfServeType: SelfServeType): Promise<Descriptor> => {
switch (selfServeType) { switch (selfServeType) {
case SelfServeTypes.example: { case SelfServeType.example: {
const SelfServeExample = await import(/* webpackChunkName: "SelfServeExample" */ "./Example/SelfServeExample"); const SelfServeExample = await import(/* webpackChunkName: "SelfServeExample" */ "./Example/SelfServeExample");
return new SelfServeExample.default().toSmartUiDescriptor(); return new SelfServeExample.default().toSmartUiDescriptor();
} }
@@ -33,20 +33,23 @@ export class SelfServeComponentAdapter implements ReactAdapter {
} }
}; };
public async renderComponent(): Promise<JSX.Element> { public renderComponent(): JSX.Element {
const selfServeType = this.container.selfServeType(); if (this.container.selfServeType() === SelfServeType.invalid) {
const smartUiDescriptor = await SelfServeComponentAdapter.getDescriptor(selfServeType); return <h1>Invalid self serve type!</h1>
}
const element = smartUiDescriptor ? ( const smartUiDescriptor = this.parameters()
return smartUiDescriptor ? (
<SmartUiComponent descriptor={smartUiDescriptor} /> <SmartUiComponent descriptor={smartUiDescriptor} />
) : ( ) : (
<h1>Invalid self serve type!</h1> <></>
); );
return element;
} }
private triggerRender() { private triggerRender() {
window.requestAnimationFrame(() => this.parameters(Date.now())); window.requestAnimationFrame(async () => {
const selfServeType = this.container.selfServeType();
const smartUiDescriptor = await SelfServeComponentAdapter.getDescriptor(selfServeType);
this.parameters(smartUiDescriptor)
});
} }
} }

View File

@@ -13,9 +13,12 @@ import {
InputType InputType
} from "../Explorer/Controls/SmartUi/SmartUiComponent"; } from "../Explorer/Controls/SmartUi/SmartUiComponent";
export enum SelfServeTypes { export enum SelfServeType {
// No self serve type passed, launch explorer
none = "none", none = "none",
// Unsupported self serve type passed as feature flag
invalid = "invalid", invalid = "invalid",
// Add your self serve types here
example = "example" example = "example"
} }

View File

@@ -1,16 +1,16 @@
import { Frame } from "puppeteer"; import { Frame } from "puppeteer";
import { TestExplorerParams } from "../testExplorer/TestExplorerParams"; import { TestExplorerParams } from "../testExplorer/TestExplorerParams";
import { getTestExplorerFrame } from "../testExplorer/TestExplorerUtils"; import { getTestExplorerFrame } from "../testExplorer/TestExplorerUtils";
import { SelfServeTypes } from "../../src/SelfServe/SelfServeUtils"; import { SelfServeType } from "../../src/SelfServe/SelfServeUtils";
jest.setTimeout(300000); jest.setTimeout(300000);
let frame: Frame; let frame: Frame;
describe("Notebook UI tests", () => { describe("Self Serve", () => {
it("Upload, Open and Delete Notebook", async () => { it("Launch Self Serve Example", async () => {
try { try {
frame = await getTestExplorerFrame( frame = await getTestExplorerFrame(
new Map<string, string>([[TestExplorerParams.selfServeType, SelfServeTypes.example]]) new Map<string, string>([[TestExplorerParams.selfServeType, SelfServeType.example]])
); );
await frame.waitForSelector(".ms-Dropdown"); await frame.waitForSelector(".ms-Dropdown");
const dropdownLabel = await frame.$eval(".ms-Dropdown-label", element => element.textContent); const dropdownLabel = await frame.$eval(".ms-Dropdown-label", element => element.textContent);