Added SelfServe UI updates (#559)

* Added SelfServe UI modifications

* fixed lint error

* addressed PR comments
This commit is contained in:
Srinath Narayanan
2021-03-18 13:40:48 -07:00
committed by GitHub
parent 9db0975f7f
commit be4e490a64
19 changed files with 800 additions and 26941 deletions

View File

@@ -1,4 +1,4 @@
import { PropertyInfo, OnChange, Values, IsDisplayable, RefreshOptions } from "../Decorators";
import { IsDisplayable, OnChange, PropertyInfo, RefreshOptions, Values } from "../Decorators";
import {
ChoiceItem,
Description,
@@ -12,14 +12,14 @@ import {
SmartUiInput,
} from "../SelfServeTypes";
import {
getMaxCollectionThroughput,
getMaxDatabaseThroughput,
getMinCollectionThroughput,
getMinDatabaseThroughput,
initialize,
onRefreshSelfServeExample,
Regions,
update,
initialize,
getMinDatabaseThroughput,
getMaxDatabaseThroughput,
getMinCollectionThroughput,
getMaxCollectionThroughput,
} from "./SelfServeExample.rp";
const regionDropdownItems: ChoiceItem[] = [
@@ -203,11 +203,7 @@ export default class SelfServeExample extends SelfServeBaseClass {
public initialize = async (): Promise<Map<string, SmartUiInput>> => {
const initializeResponse = await initialize();
const defaults = new Map<string, SmartUiInput>();
const currentRegionText = `current region selected is ${initializeResponse.regions}`;
defaults.set("currentRegionText", {
value: { textTKey: currentRegionText, type: DescriptionType.Text } as Description,
hidden: false,
});
defaults.set("currentRegionText", undefined);
defaults.set("regions", { value: initializeResponse.regions });
defaults.set("enableLogging", { value: initializeResponse.enableLogging });
const accountName = initializeResponse.accountName;

View File

@@ -1,17 +1,17 @@
import { Spinner, SpinnerSize } from "office-ui-fabric-react";
import { initializeIcons } from "office-ui-fabric-react/lib/Icons";
import * as React from "react";
import ReactDOM from "react-dom";
import { normalizeArmEndpoint } from "../Common/EnvironmentUtility";
import { sendMessage } from "../Common/MessageHandler";
import { configContext, updateConfigContext } from "../ConfigContext";
import { SelfServeFrameInputs } from "../Contracts/ViewModels";
import { updateUserContext } from "../UserContext";
import { isInvalidParentFrameOrigin } from "../Utils/MessageValidation";
import "./SelfServe.less";
import { SelfServeComponent } from "./SelfServeComponent";
import { SelfServeDescriptor } from "./SelfServeTypes";
import { SelfServeType } from "./SelfServeUtils";
import { SelfServeFrameInputs } from "../Contracts/ViewModels";
import { initializeIcons } from "office-ui-fabric-react/lib/Icons";
import { configContext, updateConfigContext } from "../ConfigContext";
import { normalizeArmEndpoint } from "../Common/EnvironmentUtility";
import { updateUserContext } from "../UserContext";
import "./SelfServe.less";
import { Spinner, SpinnerSize } from "office-ui-fabric-react";
initializeIcons();
const getDescriptor = async (selfServeType: SelfServeType): Promise<SelfServeDescriptor> => {

View File

@@ -1,34 +1,36 @@
import React from "react";
import { TFunction } from "i18next";
import {
CommandBar,
ICommandBarItemProps,
IStackTokens,
MessageBar,
MessageBarType,
Separator,
Spinner,
SpinnerSize,
Stack,
} from "office-ui-fabric-react";
import promiseRetry, { AbortError } from "p-retry";
import React from "react";
import { Translation } from "react-i18next";
import * as _ from "underscore";
import { sendMessage } from "../Common/MessageHandler";
import { SelfServeMessageTypes } from "../Contracts/SelfServeContracts";
import { SmartUiComponent, SmartUiDescriptor } from "../Explorer/Controls/SmartUi/SmartUiComponent";
import "../i18n";
import { commandBarItemStyles, commandBarStyles, containerStackTokens, separatorStyles } from "./SelfServeStyles";
import {
AnyDisplay,
Node,
BooleanInput,
ChoiceInput,
DescriptionDisplay,
InputType,
Node,
NumberInput,
RefreshResult,
SelfServeDescriptor,
SmartUiInput,
DescriptionDisplay,
StringInput,
NumberInput,
BooleanInput,
ChoiceInput,
} from "./SelfServeTypes";
import { SmartUiComponent, SmartUiDescriptor } from "../Explorer/Controls/SmartUi/SmartUiComponent";
import { Translation } from "react-i18next";
import { TFunction } from "i18next";
import "../i18n";
import { sendMessage } from "../Common/MessageHandler";
import { SelfServeMessageTypes } from "../Contracts/SelfServeContracts";
import promiseRetry, { AbortError } from "p-retry";
interface SelfServeNotification {
message: string;
@@ -127,7 +129,7 @@ export class SelfServeComponent extends React.Component<SelfServeComponentProps,
this.props.descriptor.inputNames.map((inputName) => {
let initialValue = initialValues.get(inputName);
if (!initialValue) {
initialValue = { value: undefined, hidden: false };
initialValue = { value: undefined, hidden: false, disabled: false };
}
currentValues = currentValues.set(inputName, initialValue);
baselineValues = baselineValues.set(inputName, initialValue);
@@ -311,34 +313,41 @@ export class SelfServeComponent extends React.Component<SelfServeComponentProps,
this.performSave();
};
public isDiscardButtonDisabled = (): boolean => {
if (this.state.isSaving) {
return true;
}
public isInputModified = (): boolean => {
for (const key of this.state.currentValues.keys()) {
const currentValue = JSON.stringify(this.state.currentValues.get(key));
const baselineValue = JSON.stringify(this.state.baselineValues.get(key));
const currentValue = this.state.currentValues.get(key);
if (currentValue && currentValue.hidden === undefined) {
currentValue.hidden = false;
}
if (currentValue && currentValue.disabled === undefined) {
currentValue.disabled = false;
}
if (currentValue !== baselineValue) {
return false;
const baselineValue = this.state.baselineValues.get(key);
if (baselineValue && baselineValue.hidden === undefined) {
baselineValue.hidden = false;
}
if (baselineValue && baselineValue.disabled === undefined) {
baselineValue.disabled = false;
}
if (!_.isEqual(currentValue, baselineValue)) {
return true;
}
}
return true;
return false;
};
public isRefreshing = (): boolean => {
return this.state.isSaving || this.state.isInitializing || this.state.refreshResult?.isUpdateInProgress;
};
public isDiscardButtonDisabled = (): boolean => {
return this.isRefreshing() || !this.isInputModified();
};
public isSaveButtonDisabled = (): boolean => {
if (this.state.hasErrors || this.state.isSaving) {
return true;
}
for (const key of this.state.currentValues.keys()) {
const currentValue = JSON.stringify(this.state.currentValues.get(key));
const baselineValue = JSON.stringify(this.state.baselineValues.get(key));
if (currentValue !== baselineValue) {
return false;
}
}
return true;
return this.state.hasErrors || this.isRefreshing() || !this.isInputModified();
};
private performRefresh = async (): Promise<void> => {
@@ -397,7 +406,6 @@ export class SelfServeComponent extends React.Component<SelfServeComponentProps,
key: "save",
text: this.getCommonTranslation("Save"),
iconProps: { iconName: "Save" },
split: true,
disabled: this.isSaveButtonDisabled(),
onClick: () => this.onSaveButtonClick(),
},
@@ -405,21 +413,21 @@ export class SelfServeComponent extends React.Component<SelfServeComponentProps,
key: "discard",
text: this.getCommonTranslation("Discard"),
iconProps: { iconName: "Undo" },
split: true,
disabled: this.isDiscardButtonDisabled(),
onClick: () => {
this.discard();
},
buttonStyles: commandBarItemStyles,
},
{
key: "refresh",
text: this.getCommonTranslation("Refresh"),
disabled: this.state.isInitializing,
iconProps: { iconName: "Refresh" },
split: true,
onClick: () => {
this.onRefreshClicked();
},
buttonStyles: commandBarItemStyles,
},
];
};
@@ -432,7 +440,6 @@ export class SelfServeComponent extends React.Component<SelfServeComponentProps,
};
public render(): JSX.Element {
const containerStackTokens: IStackTokens = { childrenGap: 5 };
if (this.state.compileErrorMessage) {
return <MessageBar messageBarType={MessageBarType.error}>{this.state.compileErrorMessage}</MessageBar>;
}
@@ -445,13 +452,13 @@ export class SelfServeComponent extends React.Component<SelfServeComponentProps,
return (
<div style={{ overflowX: "auto" }}>
<Stack tokens={containerStackTokens} styles={{ root: { padding: 10 } }}>
<CommandBar styles={{ root: { paddingLeft: 0 } }} items={this.getCommandBarItems()} />
<Stack tokens={containerStackTokens}>
<Stack.Item>
<CommandBar styles={commandBarStyles} items={this.getCommandBarItems()} />
<Separator styles={separatorStyles} />
</Stack.Item>
{this.state.isInitializing ? (
<Spinner
size={SpinnerSize.large}
styles={{ root: { textAlign: "center", justifyContent: "center", width: "100%", height: "100%" } }}
/>
<Spinner size={SpinnerSize.large} />
) : (
<>
{this.state.notification && (

View File

@@ -0,0 +1,20 @@
import { IButtonStyles, ICommandBarStyles, ISeparatorStyles, IStackTokens } from "office-ui-fabric-react";
import { StyleConstants } from "../Common/Constants";
export const commandBarItemStyles: IButtonStyles = { root: { paddingLeft: 20 } };
export const commandBarStyles: ICommandBarStyles = { root: { paddingLeft: 0 } };
export const containerStackTokens: IStackTokens = { childrenGap: 5, padding: 10 };
export const separatorStyles: Partial<ISeparatorStyles> = {
root: {
selectors: {
"::before": {
background: StyleConstants.BaseMedium,
},
},
padding: 0,
height: 1,
},
};

View File

@@ -1,6 +1,6 @@
import "reflect-metadata";
import { userContext } from "../UserContext";
import {
Node,
AnyDisplay,
BooleanInput,
ChoiceInput,
@@ -10,13 +10,13 @@ import {
Info,
InputType,
InputTypeValue,
Node,
NumberInput,
RefreshParams,
SelfServeDescriptor,
SmartUiInput,
StringInput,
RefreshParams,
} from "./SelfServeTypes";
import { userContext } from "../UserContext";
export enum SelfServeType {
// No self serve type passed, launch explorer
@@ -195,5 +195,5 @@ export const generateBladeLink = (blade: BladeType): string => {
const subscriptionId = userContext.subscriptionId;
const resourceGroupName = userContext.resourceGroup;
const databaseAccountName = userContext.databaseAccount.name;
return `www.portal.azure.com/#@microsoft.onmicrosoft.com/resource/subscriptions/${subscriptionId}/resourceGroups/${resourceGroupName}/providers/Microsoft.DocumentDb/databaseAccounts/${databaseAccountName}/${blade}`;
return `https://portal.azure.com/#@microsoft.onmicrosoft.com/resource/subscriptions/${subscriptionId}/resourceGroups/${resourceGroupName}/providers/Microsoft.DocumentDb/databaseAccounts/${databaseAccountName}/${blade}`;
};