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
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
19 changed files with 800 additions and 26941 deletions

26525
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -80,7 +80,7 @@
"ms": "2.1.3", "ms": "2.1.3",
"msal": "1.4.4", "msal": "1.4.4",
"object.entries": "1.1.0", "object.entries": "1.1.0",
"office-ui-fabric-react": "7.134.1", "office-ui-fabric-react": "7.164.2",
"p-retry": "4.2.0", "p-retry": "4.2.0",
"plotly.js-cartesian-dist-min": "1.52.3", "plotly.js-cartesian-dist-min": "1.52.3",
"promise-polyfill": "8.1.0", "promise-polyfill": "8.1.0",
@ -204,7 +204,7 @@
"pack:fast": "node --max_old_space_size=10196 ./node_modules/webpack/bin/webpack.js --mode development --progress", "pack:fast": "node --max_old_space_size=10196 ./node_modules/webpack/bin/webpack.js --mode development --progress",
"copyToConsumers": "node copyToConsumers", "copyToConsumers": "node copyToConsumers",
"test": "rimraf coverage && jest", "test": "rimraf coverage && jest",
"test:e2e": "jest -c ./jest.config.e2e.js --detectOpenHandles", "test:e2e": "jest -c ./jest.config.e2e.js --detectOpenHandles selfServeExample.spec.ts",
"watch": "npm run start", "watch": "npm run start",
"wait-for-server": "wait-on -t 240000 -i 5000 -v https-get://0.0.0.0:1234/", "wait-for-server": "wait-on -t 240000 -i 5000 -v https-get://0.0.0.0:1234/",
"build:ase": "gulp build:ase", "build:ase": "gulp build:ase",
@ -238,4 +238,4 @@
"prettier": { "prettier": {
"printWidth": 120 "printWidth": 120
} }
} }

View File

@ -12,7 +12,7 @@ exports[`CollapsibleSectionComponent renders 1`] = `
} }
} }
> >
<StyledIconBase <Icon
iconName="ChevronDown" iconName="ChevronDown"
styles={ styles={
Object { Object {

View File

@ -354,7 +354,6 @@ exports[`test render renders with filters 1`] = `
data-is-scrollable="true" data-is-scrollable="true"
> >
<div <div
aria-hidden="true"
className="stickyAbove-42" className="stickyAbove-42"
style={ style={
Object { Object {
@ -375,7 +374,6 @@ exports[`test render renders with filters 1`] = `
> >
<div> <div>
<div <div
aria-hidden={true}
style={ style={
Object { Object {
"pointerEvents": "none", "pointerEvents": "none",
@ -395,7 +393,6 @@ exports[`test render renders with filters 1`] = `
style={Object {}} style={Object {}}
> >
<div <div
aria-hidden={false}
style={ style={
Object { Object {
"backgroundColor": "", "backgroundColor": "",
@ -411,6 +408,7 @@ exports[`test render renders with filters 1`] = `
> >
<TextFieldBase <TextFieldBase
ariaLabel="Directory filter text box" ariaLabel="Directory filter text box"
canRevealPassword={false}
className="directoryListFilterTextBox" className="directoryListFilterTextBox"
deferredValidationTime={200} deferredValidationTime={200}
onChange={[Function]} onChange={[Function]}
@ -1123,7 +1121,7 @@ exports[`test render renders with filters 1`] = `
"iconDisabled": Object { "iconDisabled": Object {
"color": "#a19f9d", "color": "#a19f9d",
"selectors": Object { "selectors": Object {
"@media screen and (-ms-high-contrast: active)": Object { "@media screen and (-ms-high-contrast: active), (forced-colors: active)": Object {
"color": "GrayText", "color": "GrayText",
}, },
}, },
@ -1149,7 +1147,7 @@ exports[`test render renders with filters 1`] = `
"menuIconDisabled": Object { "menuIconDisabled": Object {
"color": "#a19f9d", "color": "#a19f9d",
"selectors": Object { "selectors": Object {
"@media screen and (-ms-high-contrast: active)": Object { "@media screen and (-ms-high-contrast: active), (forced-colors: active)": Object {
"color": "GrayText", "color": "GrayText",
}, },
}, },
@ -1168,7 +1166,7 @@ exports[`test render renders with filters 1`] = `
"position": "absolute", "position": "absolute",
"right": 2, "right": 2,
"selectors": Object { "selectors": Object {
"@media screen and (-ms-high-contrast: active)": Object { "@media screen and (-ms-high-contrast: active), (forced-colors: active)": Object {
"bottom": -2, "bottom": -2,
"left": -2, "left": -2,
"outlineColor": "ButtonText", "outlineColor": "ButtonText",
@ -1247,7 +1245,7 @@ exports[`test render renders with filters 1`] = `
"position": "absolute", "position": "absolute",
"right": 2, "right": 2,
"selectors": Object { "selectors": Object {
"@media screen and (-ms-high-contrast: active)": Object { "@media screen and (-ms-high-contrast: active), (forced-colors: active)": Object {
"bottom": -2, "bottom": -2,
"left": -2, "left": -2,
"outlineColor": "ButtonText", "outlineColor": "ButtonText",
@ -1279,8 +1277,10 @@ exports[`test render renders with filters 1`] = `
}, },
}, },
Object { Object {
"backgroundColor": "#f3f2f1",
"color": "#a19f9d",
"selectors": Object { "selectors": Object {
"@media screen and (-ms-high-contrast: active)": Object { "@media screen and (-ms-high-contrast: active), (forced-colors: active)": Object {
"backgroundColor": "Window", "backgroundColor": "Window",
"borderColor": "GrayText", "borderColor": "GrayText",
"color": "GrayText", "color": "GrayText",
@ -1300,7 +1300,7 @@ exports[`test render renders with filters 1`] = `
"backgroundColor": "#f3f2f1", "backgroundColor": "#f3f2f1",
"color": "#201f1e", "color": "#201f1e",
"selectors": Object { "selectors": Object {
"@media screen and (-ms-high-contrast: active)": Object { "@media screen and (-ms-high-contrast: active), (forced-colors: active)": Object {
"borderColor": "Highlight", "borderColor": "Highlight",
"color": "Highlight", "color": "Highlight",
}, },
@ -1326,7 +1326,7 @@ exports[`test render renders with filters 1`] = `
"splitButtonContainer": Array [ "splitButtonContainer": Array [
Object { Object {
"selectors": Object { "selectors": Object {
"@media screen and (-ms-high-contrast: active)": Object { "@media screen and (-ms-high-contrast: active), (forced-colors: active)": Object {
"border": "none", "border": "none",
}, },
}, },
@ -1344,7 +1344,7 @@ exports[`test render renders with filters 1`] = `
"position": "absolute", "position": "absolute",
"right": 3, "right": 3,
"selectors": Object { "selectors": Object {
"@media screen and (-ms-high-contrast: active)": Object { "@media screen and (-ms-high-contrast: active), (forced-colors: active)": Object {
"border": "none", "border": "none",
"bottom": -2, "bottom": -2,
"left": -2, "left": -2,
@ -1373,19 +1373,20 @@ exports[`test render renders with filters 1`] = `
"borderBottomRightRadius": "0", "borderBottomRightRadius": "0",
"borderTopRightRadius": "0", "borderTopRightRadius": "0",
"selectors": Object { "selectors": Object {
"@media screen and (-ms-high-contrast: active)": Object { "@media screen and (-ms-high-contrast: active), (forced-colors: active)": Object {
"MsHighContrastAdjust": "none", "MsHighContrastAdjust": "none",
"backgroundColor": "Window", "backgroundColor": "Window",
"border": "1px solid WindowText", "border": "1px solid WindowText",
"borderRightWidth": "0", "borderRightWidth": "0",
"color": "WindowText", "color": "WindowText",
"forcedColorAdjust": "none",
}, },
}, },
}, },
".ms-Button--primary + .ms-Button": Object { ".ms-Button--primary + .ms-Button": Object {
"border": "none", "border": "none",
"selectors": Object { "selectors": Object {
"@media screen and (-ms-high-contrast: active)": Object { "@media screen and (-ms-high-contrast: active), (forced-colors: active)": Object {
"border": "1px solid WindowText", "border": "1px solid WindowText",
"borderLeftWidth": "0", "borderLeftWidth": "0",
}, },
@ -1398,10 +1399,11 @@ exports[`test render renders with filters 1`] = `
"selectors": Object { "selectors": Object {
".ms-Button--primary": Object { ".ms-Button--primary": Object {
"selectors": Object { "selectors": Object {
"@media screen and (-ms-high-contrast: active)": Object { "@media screen and (-ms-high-contrast: active), (forced-colors: active)": Object {
"MsHighContrastAdjust": "none", "MsHighContrastAdjust": "none",
"backgroundColor": "WindowText", "backgroundColor": "WindowText",
"color": "Window", "color": "Window",
"forcedColorAdjust": "none",
}, },
}, },
}, },
@ -1411,10 +1413,11 @@ exports[`test render renders with filters 1`] = `
"selectors": Object { "selectors": Object {
".ms-Button--primary": Object { ".ms-Button--primary": Object {
"selectors": Object { "selectors": Object {
"@media screen and (-ms-high-contrast: active)": Object { "@media screen and (-ms-high-contrast: active), (forced-colors: active)": Object {
"MsHighContrastAdjust": "none", "MsHighContrastAdjust": "none",
"backgroundColor": "WindowText", "backgroundColor": "WindowText",
"color": "Window", "color": "Window",
"forcedColorAdjust": "none",
}, },
}, },
}, },
@ -1424,12 +1427,11 @@ exports[`test render renders with filters 1`] = `
"border": "none", "border": "none",
"outline": "none", "outline": "none",
"selectors": Object { "selectors": Object {
"@media screen and (-ms-high-contrast: active)": Object { "@media screen and (-ms-high-contrast: active), (forced-colors: active)": Object {
"MsHighContrastAdjust": "none",
"backgroundColor": "Window", "backgroundColor": "Window",
"borderColor": "GrayText", "borderColor": "GrayText",
"color": "GrayText", "color": "GrayText",
},
"@media screen and (forced-colors: active)": Object {
"forcedColorAdjust": "none", "forcedColorAdjust": "none",
}, },
}, },
@ -1441,7 +1443,7 @@ exports[`test render renders with filters 1`] = `
"selectors": Object { "selectors": Object {
".ms-Button--primary": Object { ".ms-Button--primary": Object {
"selectors": Object { "selectors": Object {
"@media screen and (-ms-high-contrast: active)": Object { "@media screen and (-ms-high-contrast: active), (forced-colors: active)": Object {
"backgroundColor": "Highlight", "backgroundColor": "Highlight",
"color": "Window", "color": "Window",
}, },
@ -1450,7 +1452,7 @@ exports[`test render renders with filters 1`] = `
".ms-Button.is-disabled": Object { ".ms-Button.is-disabled": Object {
"color": "#a19f9d", "color": "#a19f9d",
"selectors": Object { "selectors": Object {
"@media screen and (-ms-high-contrast: active)": Object { "@media screen and (-ms-high-contrast: active), (forced-colors: active)": Object {
"backgroundColor": "Window", "backgroundColor": "Window",
"borderColor": "GrayText", "borderColor": "GrayText",
"color": "GrayText", "color": "GrayText",
@ -1466,7 +1468,7 @@ exports[`test render renders with filters 1`] = `
"position": "absolute", "position": "absolute",
"right": 31, "right": 31,
"selectors": Object { "selectors": Object {
"@media screen and (-ms-high-contrast: active)": Object { "@media screen and (-ms-high-contrast: active), (forced-colors: active)": Object {
"backgroundColor": "WindowText", "backgroundColor": "WindowText",
}, },
}, },
@ -1478,7 +1480,7 @@ exports[`test render renders with filters 1`] = `
"position": "absolute", "position": "absolute",
"right": 31, "right": 31,
"selectors": Object { "selectors": Object {
"@media screen and (-ms-high-contrast: active)": Object { "@media screen and (-ms-high-contrast: active), (forced-colors: active)": Object {
"backgroundColor": "WindowText", "backgroundColor": "WindowText",
}, },
}, },
@ -1495,7 +1497,7 @@ exports[`test render renders with filters 1`] = `
"position": "absolute", "position": "absolute",
"right": 31, "right": 31,
"selectors": Object { "selectors": Object {
"@media screen and (-ms-high-contrast: active)": Object { "@media screen and (-ms-high-contrast: active), (forced-colors: active)": Object {
"backgroundColor": "GrayText", "backgroundColor": "GrayText",
}, },
}, },
@ -1518,7 +1520,7 @@ exports[`test render renders with filters 1`] = `
":hover": Object { ":hover": Object {
"backgroundColor": "#edebe9", "backgroundColor": "#edebe9",
"selectors": Object { "selectors": Object {
"@media screen and (-ms-high-contrast: active)": Object { "@media screen and (-ms-high-contrast: active), (forced-colors: active)": Object {
"color": "Highlight", "color": "Highlight",
}, },
}, },
@ -1526,6 +1528,11 @@ exports[`test render renders with filters 1`] = `
}, },
}, },
Object { Object {
"@media screen and (-ms-high-contrast: active), (forced-colors: active)": Object {
".ms-Button-menuIcon": Object {
"color": "WindowText",
},
},
"border": "1px solid #8a8886", "border": "1px solid #8a8886",
"borderBottomRightRadius": "2px", "borderBottomRightRadius": "2px",
"borderLeft": "none", "borderLeft": "none",
@ -1571,7 +1578,7 @@ exports[`test render renders with filters 1`] = `
"selectors": Object { "selectors": Object {
".ms-Button--primary": Object { ".ms-Button--primary": Object {
"selectors": Object { "selectors": Object {
"@media screen and (-ms-high-contrast: active)": Object { "@media screen and (-ms-high-contrast: active), (forced-colors: active)": Object {
"backgroundColor": "Window", "backgroundColor": "Window",
"borderColor": "GrayText", "borderColor": "GrayText",
"color": "GrayText", "color": "GrayText",
@ -1580,7 +1587,7 @@ exports[`test render renders with filters 1`] = `
}, },
".ms-Button-menuIcon": Object { ".ms-Button-menuIcon": Object {
"selectors": Object { "selectors": Object {
"@media screen and (-ms-high-contrast: active)": Object { "@media screen and (-ms-high-contrast: active), (forced-colors: active)": Object {
"color": "GrayText", "color": "GrayText",
}, },
}, },
@ -1588,7 +1595,7 @@ exports[`test render renders with filters 1`] = `
":hover": Object { ":hover": Object {
"cursor": "default", "cursor": "default",
}, },
"@media screen and (-ms-high-contrast: active)": Object { "@media screen and (-ms-high-contrast: active), (forced-colors: active)": Object {
"backgroundColor": "Window", "backgroundColor": "Window",
"border": "1px solid GrayText", "border": "1px solid GrayText",
"color": "GrayText", "color": "GrayText",
@ -1893,7 +1900,7 @@ exports[`test render renders with filters 1`] = `
> >
<button <button
aria-disabled={true} aria-disabled={true}
className="ms-Button ms-Button--default is-disabled directoryListButton root-54" className="ms-Button ms-Button--default is-disabled directoryListButton root-57"
data-is-focusable={false} data-is-focusable={false}
disabled={true} disabled={true}
onClick={[Function]} onClick={[Function]}
@ -1905,7 +1912,7 @@ exports[`test render renders with filters 1`] = `
type="button" type="button"
> >
<span <span
className="ms-Button-flexContainer flexContainer-55" className="ms-Button-flexContainer flexContainer-58"
data-automationid="splitbuttonprimary" data-automationid="splitbuttonprimary"
> >
<div <div
@ -1936,7 +1943,6 @@ exports[`test render renders with filters 1`] = `
</List> </List>
</div> </div>
<div <div
aria-hidden="true"
className="stickyBelow-43" className="stickyBelow-43"
style={ style={
Object { Object {

View File

@ -107,7 +107,7 @@ exports[`GalleryCardComponent renders 1`] = `
} }
variant="tiny" variant="tiny"
> >
<StyledIconBase <Icon
iconName="RedEye" iconName="RedEye"
styles={ styles={
Object { Object {
@ -131,7 +131,7 @@ exports[`GalleryCardComponent renders 1`] = `
} }
variant="tiny" variant="tiny"
> >
<StyledIconBase <Icon
iconName="Download" iconName="Download"
styles={ styles={
Object { Object {
@ -155,7 +155,7 @@ exports[`GalleryCardComponent renders 1`] = `
} }
variant="tiny" variant="tiny"
> >
<StyledIconBase <Icon
iconName="Heart" iconName="Heart"
styles={ styles={
Object { Object {
@ -180,7 +180,7 @@ exports[`GalleryCardComponent renders 1`] = `
} }
} }
> >
<Styled <Separator
styles={ styles={
Object { Object {
"root": Object { "root": Object {

View File

@ -13,7 +13,7 @@ exports[`InfoComponent renders 1`] = `
<div <div
className="infoPanelMain" className="infoPanelMain"
> >
<StyledIconBase <Icon
className="infoIconMain" className="infoIconMain"
iconName="Help" iconName="Help"
styles={ styles={

View File

@ -68,14 +68,14 @@ exports[`NotebookMetadataComponent renders liked notebook 1`] = `
Invalid Date Invalid Date
</Text> </Text>
<Text> <Text>
<StyledIconBase <Icon
iconName="RedEye" iconName="RedEye"
/> />
0 0
</Text> </Text>
<Text> <Text>
<StyledIconBase <Icon
iconName="Download" iconName="Download"
/> />
0 0
@ -180,14 +180,14 @@ exports[`NotebookMetadataComponent renders un-liked notebook 1`] = `
Invalid Date Invalid Date
</Text> </Text>
<Text> <Text>
<StyledIconBase <Icon
iconName="RedEye" iconName="RedEye"
/> />
0 0
</Text> </Text>
<Text> <Text>
<StyledIconBase <Icon
iconName="Download" iconName="Download"
/> />
0 0

View File

@ -114,7 +114,7 @@ exports[`MongoIndexingPolicyComponent renders 1`] = `
</Stack> </Stack>
</CollapsibleSectionComponent> </CollapsibleSectionComponent>
</Stack> </Stack>
<Styled <Separator
styles={ styles={
Object { Object {
"root": Array [ "root": Array [

View File

@ -41,7 +41,7 @@ exports[`ToolTipLabelComponent renders 1`] = `
} }
} }
> >
<StyledIconBase <Icon
ariaLabel="Info" ariaLabel="Info"
iconName="Info" iconName="Info"
styles={ styles={

View File

@ -1,14 +1,13 @@
import * as React from "react"; import { TFunction } from "i18next";
import { Position } from "office-ui-fabric-react/lib/utilities/positioning"; import { Label, Link, MessageBar, MessageBarType, Toggle } from "office-ui-fabric-react";
import { Dropdown, IDropdownOption } from "office-ui-fabric-react/lib/Dropdown";
import { Slider } from "office-ui-fabric-react/lib/Slider"; import { Slider } from "office-ui-fabric-react/lib/Slider";
import { SpinButton } from "office-ui-fabric-react/lib/SpinButton"; import { SpinButton } from "office-ui-fabric-react/lib/SpinButton";
import { Dropdown, IDropdownOption } from "office-ui-fabric-react/lib/Dropdown"; import { IStackTokens, Stack } from "office-ui-fabric-react/lib/Stack";
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 { Stack, IStackTokens } from "office-ui-fabric-react/lib/Stack"; import { TextField } from "office-ui-fabric-react/lib/TextField";
import { Label, Link, MessageBar, MessageBarType, Toggle } from "office-ui-fabric-react"; import { Position } from "office-ui-fabric-react/lib/utilities/positioning";
import * as InputUtils from "./InputUtils"; import * as React from "react";
import "./SmartUiComponent.less";
import { import {
ChoiceItem, ChoiceItem,
Description, Description,
@ -19,8 +18,9 @@ import {
NumberUiType, NumberUiType,
SmartUiInput, SmartUiInput,
} from "../../../SelfServe/SelfServeTypes"; } from "../../../SelfServe/SelfServeTypes";
import { TFunction } from "i18next";
import { ToolTipLabelComponent } from "../Settings/SettingsSubComponents/ToolTipLabelComponent"; import { ToolTipLabelComponent } from "../Settings/SettingsSubComponents/ToolTipLabelComponent";
import * as InputUtils from "./InputUtils";
import "./SmartUiComponent.less";
/** /**
* Generic UX renderer * Generic UX renderer
@ -138,11 +138,12 @@ export class SmartUiComponent extends React.Component<SmartUiComponentProps, Sma
); );
} }
private renderTextInput(input: StringInput, labelId: string): JSX.Element { private renderTextInput(input: StringInput, labelId: string, labelElement: JSX.Element): JSX.Element {
const value = this.props.currentValues.get(input.dataFieldName)?.value as string; const value = this.props.currentValues.get(input.dataFieldName)?.value as string;
const disabled = this.props.disabled || this.props.currentValues.get(input.dataFieldName)?.disabled; const disabled = this.props.disabled || this.props.currentValues.get(input.dataFieldName)?.disabled;
return ( return (
<div className="stringInputContainer"> <Stack>
{labelElement}
<TextField <TextField
id={`${input.dataFieldName}-textField-input`} id={`${input.dataFieldName}-textField-input`}
aria-labelledby={labelId} aria-labelledby={labelId}
@ -155,25 +156,32 @@ export class SmartUiComponent extends React.Component<SmartUiComponentProps, Sma
root: { width: 400 }, root: { width: 400 },
}} }}
/> />
</div> </Stack>
); );
} }
private renderDescription(input: DescriptionDisplay, labelId: string): JSX.Element { private renderDescription(input: DescriptionDisplay, labelId: string, labelElement: JSX.Element): JSX.Element {
const dataFieldName = input.dataFieldName; const dataFieldName = input.dataFieldName;
const description = input.description || (this.props.currentValues.get(dataFieldName)?.value as Description); const description = input.description || (this.props.currentValues.get(dataFieldName)?.value as Description);
if (!description) { if (!description) {
return this.renderError("Description is not provided."); if (!input.isDynamicDescription) {
return this.renderError("Description is not provided.");
}
// If input is a dynamic description and description is not available, empty element is rendered
return <></>;
} }
const descriptionElement = ( const descriptionElement = (
<Text id={`${dataFieldName}-text-display`} aria-labelledby={labelId}> <Stack>
{this.props.getTranslation(description.textTKey)}{" "} {labelElement}
{description.link && ( <Text id={`${dataFieldName}-text-display`} aria-labelledby={labelId}>
<Link target="_blank" href={description.link.href}> {this.props.getTranslation(description.textTKey)}{" "}
{this.props.getTranslation(description.link.textTKey)} {description.link && (
</Link> <Link target="_blank" href={description.link.href}>
)} {this.props.getTranslation(description.link.textTKey)}
</Text> </Link>
)}
</Text>
</Stack>
); );
if (description.type === DescriptionType.Text) { if (description.type === DescriptionType.Text) {
@ -227,7 +235,7 @@ export class SmartUiComponent extends React.Component<SmartUiComponentProps, Sma
return undefined; return undefined;
}; };
private renderNumberInput(input: NumberInput, labelId: string): JSX.Element { private renderNumberInput(input: NumberInput, labelId: string, labelElement: JSX.Element): JSX.Element {
const { labelTKey, min, max, dataFieldName, step } = input; const { labelTKey, min, max, dataFieldName, step } = input;
const props = { const props = {
min: min, min: min,
@ -240,61 +248,72 @@ export class SmartUiComponent extends React.Component<SmartUiComponentProps, Sma
const disabled = this.props.disabled || this.props.currentValues.get(dataFieldName)?.disabled; const disabled = this.props.disabled || this.props.currentValues.get(dataFieldName)?.disabled;
if (input.uiType === NumberUiType.Spinner) { if (input.uiType === NumberUiType.Spinner) {
return ( return (
<Stack styles={{ root: { width: 400 } }} tokens={{ childrenGap: 2 }}> <Stack>
<SpinButton {labelElement}
{...props} <Stack styles={{ root: { width: 400 } }} tokens={{ childrenGap: 2 }}>
id={`${input.dataFieldName}-spinner-input`} <SpinButton
value={value?.toString()} {...props}
onValidate={(newValue) => this.onValidate(input, newValue, props.min, props.max)} id={`${input.dataFieldName}-spinner-input`}
onIncrement={(newValue) => this.onIncrement(input, newValue, props.step, props.max)} value={value?.toString()}
onDecrement={(newValue) => this.onDecrement(input, newValue, props.step, props.min)} onValidate={(newValue) => this.onValidate(input, newValue, props.min, props.max)}
labelPosition={Position.top} onIncrement={(newValue) => this.onIncrement(input, newValue, props.step, props.max)}
aria-labelledby={labelId} onDecrement={(newValue) => this.onDecrement(input, newValue, props.step, props.min)}
disabled={disabled} labelPosition={Position.top}
/> aria-labelledby={labelId}
{this.state.errors.has(dataFieldName) && ( disabled={disabled}
<MessageBar messageBarType={MessageBarType.error}>Error: {this.state.errors.get(dataFieldName)}</MessageBar> />
)} {this.state.errors.has(dataFieldName) && (
<MessageBar messageBarType={MessageBarType.error}>
Error: {this.state.errors.get(dataFieldName)}
</MessageBar>
)}
</Stack>
</Stack> </Stack>
); );
} else if (input.uiType === NumberUiType.Slider) { } else if (input.uiType === NumberUiType.Slider) {
return ( return (
<div id={`${input.dataFieldName}-slider-input`}> <Stack>
<Slider {labelElement}
{...props} <div id={`${input.dataFieldName}-slider-input`}>
value={value} <Slider
disabled={disabled} {...props}
onChange={(newValue) => this.props.onInputChange(input, newValue)} value={value}
styles={{ disabled={disabled}
root: { width: 400 }, onChange={(newValue) => this.props.onInputChange(input, newValue)}
valueLabel: SmartUiComponent.labelStyle, styles={{
}} root: { width: 400 },
/> valueLabel: SmartUiComponent.labelStyle,
</div> }}
/>
</div>
</Stack>
); );
} else { } else {
return <>Unsupported number UI type {input.uiType}</>; return <>Unsupported number UI type {input.uiType}</>;
} }
} }
private renderBooleanInput(input: BooleanInput, labelId: string): JSX.Element { private renderBooleanInput(input: BooleanInput, labelId: string, labelElement: JSX.Element): JSX.Element {
const value = this.props.currentValues.get(input.dataFieldName)?.value as boolean; const value = this.props.currentValues.get(input.dataFieldName)?.value as boolean;
const disabled = this.props.disabled || this.props.currentValues.get(input.dataFieldName)?.disabled; const disabled = this.props.disabled || this.props.currentValues.get(input.dataFieldName)?.disabled;
return ( return (
<Toggle <Stack>
id={`${input.dataFieldName}-toggle-input`} {labelElement}
aria-labelledby={labelId} <Toggle
checked={value || false} id={`${input.dataFieldName}-toggle-input`}
onText={this.props.getTranslation(input.trueLabelTKey)} aria-labelledby={labelId}
offText={this.props.getTranslation(input.falseLabelTKey)} checked={value || false}
disabled={disabled} onText={this.props.getTranslation(input.trueLabelTKey)}
onChange={(event, checked: boolean) => this.props.onInputChange(input, checked)} offText={this.props.getTranslation(input.falseLabelTKey)}
styles={{ root: { width: 400 } }} disabled={disabled}
/> onChange={(event, checked: boolean) => this.props.onInputChange(input, checked)}
styles={{ root: { width: 400 } }}
/>
</Stack>
); );
} }
private renderChoiceInput(input: ChoiceInput, labelId: string): JSX.Element { private renderChoiceInput(input: ChoiceInput, labelId: string, labelElement: JSX.Element): JSX.Element {
const { defaultKey, dataFieldName, choices, placeholderTKey } = input; const { defaultKey, dataFieldName, choices, placeholderTKey } = input;
const value = this.props.currentValues.get(dataFieldName)?.value as string; const value = this.props.currentValues.get(dataFieldName)?.value as string;
const disabled = this.props.disabled || this.props.currentValues.get(dataFieldName)?.disabled; const disabled = this.props.disabled || this.props.currentValues.get(dataFieldName)?.disabled;
@ -303,22 +322,26 @@ export class SmartUiComponent extends React.Component<SmartUiComponentProps, Sma
selectedKey = ""; selectedKey = "";
} }
return ( return (
<Dropdown <Stack>
id={`${input.dataFieldName}-dropdown-input`} {labelElement}
aria-labelledby={labelId} <Dropdown
selectedKey={selectedKey} id={`${input.dataFieldName}-dropdown-input`}
onChange={(_, item: IDropdownOption) => this.props.onInputChange(input, item.key.toString())} aria-labelledby={labelId}
placeholder={this.props.getTranslation(placeholderTKey)} selectedKey={selectedKey}
disabled={disabled} onChange={(_, item: IDropdownOption) => this.props.onInputChange(input, item.key.toString())}
options={choices.map((c) => ({ placeholder={this.props.getTranslation(placeholderTKey)}
key: c.key, disabled={disabled}
text: this.props.getTranslation(c.label), dropdownWidth="auto"
}))} options={choices.map((c) => ({
styles={{ key: c.key,
root: { width: 400 }, text: this.props.getTranslation(c.label),
dropdown: SmartUiComponent.labelStyle, }))}
}} styles={{
/> root: { width: 400 },
dropdown: SmartUiComponent.labelStyle,
}}
/>
</Stack>
); );
} }
@ -326,7 +349,7 @@ export class SmartUiComponent extends React.Component<SmartUiComponentProps, Sma
return <MessageBar messageBarType={MessageBarType.error}>Error: {errorMessage}</MessageBar>; return <MessageBar messageBarType={MessageBarType.error}>Error: {errorMessage}</MessageBar>;
} }
private renderDisplayWithInfoBubble(input: AnyDisplay, info: Info): JSX.Element { private renderElement(input: AnyDisplay, info: Info): JSX.Element {
if (input.errorMessage) { if (input.errorMessage) {
return this.renderError(input.errorMessage); return this.renderError(input.errorMessage);
} }
@ -335,34 +358,31 @@ export class SmartUiComponent extends React.Component<SmartUiComponentProps, Sma
return <></>; return <></>;
} }
const labelId = `${input.dataFieldName}-label`; const labelId = `${input.dataFieldName}-label`;
return ( const labelElement: JSX.Element = input.labelTKey && (
<Stack> <Label id={labelId}>
{input.labelTKey && ( <ToolTipLabelComponent
<Label id={labelId}> label={this.props.getTranslation(input.labelTKey)}
<ToolTipLabelComponent toolTipElement={this.renderInfo(info)}
label={this.props.getTranslation(input.labelTKey)} />
toolTipElement={this.renderInfo(info)} </Label>
/>
</Label>
)}
{this.renderDisplay(input, labelId)}
</Stack>
); );
return <Stack>{this.renderControl(input, labelId, labelElement)}</Stack>;
} }
private renderDisplay(input: AnyDisplay, labelId: string): JSX.Element { private renderControl(input: AnyDisplay, labelId: string, labelElement: JSX.Element): JSX.Element {
switch (input.type) { switch (input.type) {
case "string": case "string":
if ("description" in input || "isDynamicDescription" in input) { if ("description" in input || "isDynamicDescription" in input) {
return this.renderDescription(input as DescriptionDisplay, labelId); return this.renderDescription(input as DescriptionDisplay, labelId, labelElement);
} }
return this.renderTextInput(input as StringInput, labelId); return this.renderTextInput(input as StringInput, labelId, labelElement);
case "number": case "number":
return this.renderNumberInput(input as NumberInput, labelId); return this.renderNumberInput(input as NumberInput, labelId, labelElement);
case "boolean": case "boolean":
return this.renderBooleanInput(input as BooleanInput, labelId); return this.renderBooleanInput(input as BooleanInput, labelId, labelElement);
case "object": case "object":
return this.renderChoiceInput(input as ChoiceInput, labelId); return this.renderChoiceInput(input as ChoiceInput, labelId, labelElement);
default: default:
throw new Error(`Unknown input type: ${input.type}`); throw new Error(`Unknown input type: ${input.type}`);
} }
@ -373,7 +393,7 @@ export class SmartUiComponent extends React.Component<SmartUiComponentProps, Sma
return ( return (
<Stack tokens={containerStackTokens} className="widgetRendererContainer"> <Stack tokens={containerStackTokens} className="widgetRendererContainer">
<Stack.Item>{node.input && this.renderDisplayWithInfoBubble(node.input, node.info as Info)}</Stack.Item> <Stack.Item>{node.input && this.renderElement(node.input, node.info as Info)}</Stack.Item>
{node.children && node.children.map((child) => <div key={child.id}>{this.renderNode(child)}</div>)} {node.children && node.children.map((child) => <div key={child.id}>{this.renderNode(child)}</div>)}
</Stack> </Stack>
); );

View File

@ -23,19 +23,21 @@ exports[`SmartUiComponent disable all inputs 1`] = `
> >
<StackItem> <StackItem>
<Stack> <Stack>
<Text <Stack>
aria-labelledby="description-label" <Text
id="description-text-display" aria-labelledby="description-label"
> id="description-text-display"
this is an example description text.
<StyledLinkBase
href="https://docs.microsoft.com/en-us/azure/cosmos-db/introduction"
target="_blank"
> >
Click here for more information. this is an example description text.
</StyledLinkBase>
</Text> <StyledLinkBase
href="https://docs.microsoft.com/en-us/azure/cosmos-db/introduction"
target="_blank"
>
Click here for more information.
</StyledLinkBase>
</Text>
</Stack>
</Stack> </Stack>
</StackItem> </StackItem>
</Stack> </Stack>
@ -53,51 +55,53 @@ exports[`SmartUiComponent disable all inputs 1`] = `
> >
<StackItem> <StackItem>
<Stack> <Stack>
<StyledLabelBase <Stack>
id="throughput-label" <StyledLabelBase
> id="throughput-label"
<ToolTipLabelComponent >
label="Throughput (input)" <ToolTipLabelComponent
/> label="Throughput (input)"
</StyledLabelBase> />
<Stack </StyledLabelBase>
styles={ <Stack
Object { styles={
"root": Object {
"width": 400,
},
}
}
tokens={
Object {
"childrenGap": 2,
}
}
>
<CustomizedSpinButton
aria-labelledby="throughput-label"
ariaLabel="Throughput (input)"
decrementButtonIcon={
Object { Object {
"iconName": "ChevronDownSmall", "root": Object {
"width": 400,
},
} }
} }
disabled={true} tokens={
id="throughput-spinner-input"
incrementButtonIcon={
Object { Object {
"iconName": "ChevronUpSmall", "childrenGap": 2,
} }
} }
label="" >
labelPosition={0} <CustomizedSpinButton
max={500} aria-labelledby="throughput-label"
min={400} ariaLabel="Throughput (input)"
onDecrement={[Function]} decrementButtonIcon={
onIncrement={[Function]} Object {
onValidate={[Function]} "iconName": "ChevronDownSmall",
step={10} }
/> }
disabled={true}
id="throughput-spinner-input"
incrementButtonIcon={
Object {
"iconName": "ChevronUpSmall",
}
}
label=""
labelPosition={0}
max={500}
min={400}
onDecrement={[Function]}
onIncrement={[Function]}
onValidate={[Function]}
step={10}
/>
</Stack>
</Stack> </Stack>
</Stack> </Stack>
</StackItem> </StackItem>
@ -116,37 +120,39 @@ exports[`SmartUiComponent disable all inputs 1`] = `
> >
<StackItem> <StackItem>
<Stack> <Stack>
<StyledLabelBase <Stack>
id="throughput2-label" <StyledLabelBase
> id="throughput2-label"
<ToolTipLabelComponent >
label="Throughput (Slider)" <ToolTipLabelComponent
/> label="Throughput (Slider)"
</StyledLabelBase> />
<div </StyledLabelBase>
id="throughput2-slider-input" <div
> id="throughput2-slider-input"
<StyledSliderBase >
ariaLabel="Throughput (Slider)" <StyledSliderBase
disabled={true} ariaLabel="Throughput (Slider)"
max={500} disabled={true}
min={400} max={500}
onChange={[Function]} min={400}
step={10} onChange={[Function]}
styles={ step={10}
Object { styles={
"root": Object { Object {
"width": 400, "root": Object {
}, "width": 400,
"valueLabel": Object { },
"color": "#393939", "valueLabel": Object {
"fontFamily": "wf_segoe-ui_normal, 'Segoe UI', 'Segoe WP', Tahoma, Arial, sans-serif", "color": "#393939",
"fontSize": 12, "fontFamily": "wf_segoe-ui_normal, 'Segoe UI', 'Segoe WP', Tahoma, Arial, sans-serif",
}, "fontSize": 12,
},
}
} }
} />
/> </div>
</div> </Stack>
</Stack> </Stack>
</StackItem> </StackItem>
</Stack> </Stack>
@ -185,16 +191,14 @@ exports[`SmartUiComponent disable all inputs 1`] = `
> >
<StackItem> <StackItem>
<Stack> <Stack>
<StyledLabelBase <Stack>
id="containerId-label" <StyledLabelBase
> id="containerId-label"
<ToolTipLabelComponent >
label="Container id" <ToolTipLabelComponent
/> label="Container id"
</StyledLabelBase> />
<div </StyledLabelBase>
className="stringInputContainer"
>
<StyledTextFieldBase <StyledTextFieldBase
aria-labelledby="containerId-label" aria-labelledby="containerId-label"
disabled={true} disabled={true}
@ -210,7 +214,7 @@ exports[`SmartUiComponent disable all inputs 1`] = `
type="text" type="text"
value="" value=""
/> />
</div> </Stack>
</Stack> </Stack>
</StackItem> </StackItem>
</Stack> </Stack>
@ -228,29 +232,31 @@ exports[`SmartUiComponent disable all inputs 1`] = `
> >
<StackItem> <StackItem>
<Stack> <Stack>
<StyledLabelBase <Stack>
id="analyticalStore-label" <StyledLabelBase
> id="analyticalStore-label"
<ToolTipLabelComponent >
label="Analytical Store" <ToolTipLabelComponent
/> label="Analytical Store"
</StyledLabelBase> />
<StyledToggleBase </StyledLabelBase>
aria-labelledby="analyticalStore-label" <StyledToggleBase
checked={false} aria-labelledby="analyticalStore-label"
disabled={true} checked={false}
id="analyticalStore-toggle-input" disabled={true}
offText="Disabled" id="analyticalStore-toggle-input"
onChange={[Function]} offText="Disabled"
onText="Enabled" onChange={[Function]}
styles={ onText="Enabled"
Object { styles={
"root": Object { Object {
"width": 400, "root": Object {
}, "width": 400,
},
}
} }
} />
/> </Stack>
</Stack> </Stack>
</StackItem> </StackItem>
</Stack> </Stack>
@ -268,48 +274,51 @@ exports[`SmartUiComponent disable all inputs 1`] = `
> >
<StackItem> <StackItem>
<Stack> <Stack>
<StyledLabelBase <Stack>
id="database-label" <StyledLabelBase
> id="database-label"
<ToolTipLabelComponent >
label="Database" <ToolTipLabelComponent
/> label="Database"
</StyledLabelBase> />
<StyledWithResponsiveMode </StyledLabelBase>
aria-labelledby="database-label" <StyledWithResponsiveMode
disabled={true} aria-labelledby="database-label"
id="database-dropdown-input" disabled={true}
onChange={[Function]} dropdownWidth="auto"
options={ id="database-dropdown-input"
Array [ onChange={[Function]}
Object { options={
"key": "db1", Array [
"text": "Database 1", Object {
}, "key": "db1",
Object { "text": "Database 1",
"key": "db2", },
"text": "Database 2", Object {
}, "key": "db2",
Object { "text": "Database 2",
"key": "db3", },
"text": "Database 3", Object {
}, "key": "db3",
] "text": "Database 3",
} },
selectedKey="db2" ]
styles={
Object {
"dropdown": Object {
"color": "#393939",
"fontFamily": "wf_segoe-ui_normal, 'Segoe UI', 'Segoe WP', Tahoma, Arial, sans-serif",
"fontSize": 12,
},
"root": Object {
"width": 400,
},
} }
} selectedKey="db2"
/> styles={
Object {
"dropdown": Object {
"color": "#393939",
"fontFamily": "wf_segoe-ui_normal, 'Segoe UI', 'Segoe WP', Tahoma, Arial, sans-serif",
"fontSize": 12,
},
"root": Object {
"width": 400,
},
}
}
/>
</Stack>
</Stack> </Stack>
</StackItem> </StackItem>
</Stack> </Stack>
@ -340,19 +349,21 @@ exports[`SmartUiComponent should render and honor input's hidden, disabled state
> >
<StackItem> <StackItem>
<Stack> <Stack>
<Text <Stack>
aria-labelledby="description-label" <Text
id="description-text-display" aria-labelledby="description-label"
> id="description-text-display"
this is an example description text.
<StyledLinkBase
href="https://docs.microsoft.com/en-us/azure/cosmos-db/introduction"
target="_blank"
> >
Click here for more information. this is an example description text.
</StyledLinkBase>
</Text> <StyledLinkBase
href="https://docs.microsoft.com/en-us/azure/cosmos-db/introduction"
target="_blank"
>
Click here for more information.
</StyledLinkBase>
</Text>
</Stack>
</Stack> </Stack>
</StackItem> </StackItem>
</Stack> </Stack>
@ -370,51 +381,53 @@ exports[`SmartUiComponent should render and honor input's hidden, disabled state
> >
<StackItem> <StackItem>
<Stack> <Stack>
<StyledLabelBase <Stack>
id="throughput-label" <StyledLabelBase
> id="throughput-label"
<ToolTipLabelComponent >
label="Throughput (input)" <ToolTipLabelComponent
/> label="Throughput (input)"
</StyledLabelBase> />
<Stack </StyledLabelBase>
styles={ <Stack
Object { styles={
"root": Object {
"width": 400,
},
}
}
tokens={
Object {
"childrenGap": 2,
}
}
>
<CustomizedSpinButton
aria-labelledby="throughput-label"
ariaLabel="Throughput (input)"
decrementButtonIcon={
Object { Object {
"iconName": "ChevronDownSmall", "root": Object {
"width": 400,
},
} }
} }
disabled={false} tokens={
id="throughput-spinner-input"
incrementButtonIcon={
Object { Object {
"iconName": "ChevronUpSmall", "childrenGap": 2,
} }
} }
label="" >
labelPosition={0} <CustomizedSpinButton
max={500} aria-labelledby="throughput-label"
min={400} ariaLabel="Throughput (input)"
onDecrement={[Function]} decrementButtonIcon={
onIncrement={[Function]} Object {
onValidate={[Function]} "iconName": "ChevronDownSmall",
step={10} }
/> }
disabled={false}
id="throughput-spinner-input"
incrementButtonIcon={
Object {
"iconName": "ChevronUpSmall",
}
}
label=""
labelPosition={0}
max={500}
min={400}
onDecrement={[Function]}
onIncrement={[Function]}
onValidate={[Function]}
step={10}
/>
</Stack>
</Stack> </Stack>
</Stack> </Stack>
</StackItem> </StackItem>
@ -433,36 +446,38 @@ exports[`SmartUiComponent should render and honor input's hidden, disabled state
> >
<StackItem> <StackItem>
<Stack> <Stack>
<StyledLabelBase <Stack>
id="throughput2-label" <StyledLabelBase
> id="throughput2-label"
<ToolTipLabelComponent >
label="Throughput (Slider)" <ToolTipLabelComponent
/> label="Throughput (Slider)"
</StyledLabelBase> />
<div </StyledLabelBase>
id="throughput2-slider-input" <div
> id="throughput2-slider-input"
<StyledSliderBase >
ariaLabel="Throughput (Slider)" <StyledSliderBase
max={500} ariaLabel="Throughput (Slider)"
min={400} max={500}
onChange={[Function]} min={400}
step={10} onChange={[Function]}
styles={ step={10}
Object { styles={
"root": Object { Object {
"width": 400, "root": Object {
}, "width": 400,
"valueLabel": Object { },
"color": "#393939", "valueLabel": Object {
"fontFamily": "wf_segoe-ui_normal, 'Segoe UI', 'Segoe WP', Tahoma, Arial, sans-serif", "color": "#393939",
"fontSize": 12, "fontFamily": "wf_segoe-ui_normal, 'Segoe UI', 'Segoe WP', Tahoma, Arial, sans-serif",
}, "fontSize": 12,
},
}
} }
} />
/> </div>
</div> </Stack>
</Stack> </Stack>
</StackItem> </StackItem>
</Stack> </Stack>
@ -501,16 +516,14 @@ exports[`SmartUiComponent should render and honor input's hidden, disabled state
> >
<StackItem> <StackItem>
<Stack> <Stack>
<StyledLabelBase <Stack>
id="containerId-label" <StyledLabelBase
> id="containerId-label"
<ToolTipLabelComponent >
label="Container id" <ToolTipLabelComponent
/> label="Container id"
</StyledLabelBase> />
<div </StyledLabelBase>
className="stringInputContainer"
>
<StyledTextFieldBase <StyledTextFieldBase
aria-labelledby="containerId-label" aria-labelledby="containerId-label"
id="containerId-textField-input" id="containerId-textField-input"
@ -525,7 +538,7 @@ exports[`SmartUiComponent should render and honor input's hidden, disabled state
type="text" type="text"
value="" value=""
/> />
</div> </Stack>
</Stack> </Stack>
</StackItem> </StackItem>
</Stack> </Stack>
@ -543,28 +556,30 @@ exports[`SmartUiComponent should render and honor input's hidden, disabled state
> >
<StackItem> <StackItem>
<Stack> <Stack>
<StyledLabelBase <Stack>
id="analyticalStore-label" <StyledLabelBase
> id="analyticalStore-label"
<ToolTipLabelComponent >
label="Analytical Store" <ToolTipLabelComponent
/> label="Analytical Store"
</StyledLabelBase> />
<StyledToggleBase </StyledLabelBase>
aria-labelledby="analyticalStore-label" <StyledToggleBase
checked={false} aria-labelledby="analyticalStore-label"
id="analyticalStore-toggle-input" checked={false}
offText="Disabled" id="analyticalStore-toggle-input"
onChange={[Function]} offText="Disabled"
onText="Enabled" onChange={[Function]}
styles={ onText="Enabled"
Object { styles={
"root": Object { Object {
"width": 400, "root": Object {
}, "width": 400,
},
}
} }
} />
/> </Stack>
</Stack> </Stack>
</StackItem> </StackItem>
</Stack> </Stack>
@ -582,47 +597,50 @@ exports[`SmartUiComponent should render and honor input's hidden, disabled state
> >
<StackItem> <StackItem>
<Stack> <Stack>
<StyledLabelBase <Stack>
id="database-label" <StyledLabelBase
> id="database-label"
<ToolTipLabelComponent >
label="Database" <ToolTipLabelComponent
/> label="Database"
</StyledLabelBase> />
<StyledWithResponsiveMode </StyledLabelBase>
aria-labelledby="database-label" <StyledWithResponsiveMode
id="database-dropdown-input" aria-labelledby="database-label"
onChange={[Function]} dropdownWidth="auto"
options={ id="database-dropdown-input"
Array [ onChange={[Function]}
Object { options={
"key": "db1", Array [
"text": "Database 1", Object {
}, "key": "db1",
Object { "text": "Database 1",
"key": "db2", },
"text": "Database 2", Object {
}, "key": "db2",
Object { "text": "Database 2",
"key": "db3", },
"text": "Database 3", Object {
}, "key": "db3",
] "text": "Database 3",
} },
selectedKey="db2" ]
styles={
Object {
"dropdown": Object {
"color": "#393939",
"fontFamily": "wf_segoe-ui_normal, 'Segoe UI', 'Segoe WP', Tahoma, Arial, sans-serif",
"fontSize": 12,
},
"root": Object {
"width": 400,
},
} }
} selectedKey="db2"
/> styles={
Object {
"dropdown": Object {
"color": "#393939",
"fontFamily": "wf_segoe-ui_normal, 'Segoe UI', 'Segoe WP', Tahoma, Arial, sans-serif",
"fontSize": 12,
},
"root": Object {
"width": 400,
},
}
}
/>
</Stack>
</Stack> </Stack>
</StackItem> </StackItem>
</Stack> </Stack>

View File

@ -310,7 +310,7 @@ exports[`Delete Collection Confirmation Pane submit() should call delete collect
> >
<i <i
aria-hidden={true} aria-hidden={true}
className="panelWarningIcon root-109" className="panelWarningIcon root-141"
data-icon-name="WarningSolid" data-icon-name="WarningSolid"
> >
@ -325,7 +325,7 @@ exports[`Delete Collection Confirmation Pane submit() should call delete collect
variant="small" variant="small"
> >
<span <span
className="panelWarningErrorMessage css-110" className="panelWarningErrorMessage css-142"
> >
Warning! The action you are about to take cannot be undone. Continuing will permanently delete this resource and all of its children resources. Warning! The action you are about to take cannot be undone. Continuing will permanently delete this resource and all of its children resources.
</span> </span>
@ -348,7 +348,7 @@ exports[`Delete Collection Confirmation Pane submit() should call delete collect
variant="small" variant="small"
> >
<span <span
className="css-110" className="css-142"
> >
Confirm by typing the collection id Confirm by typing the collection id
</span> </span>
@ -367,6 +367,7 @@ exports[`Delete Collection Confirmation Pane submit() should call delete collect
> >
<TextFieldBase <TextFieldBase
autoFocus={true} autoFocus={true}
canRevealPassword={false}
deferredValidationTime={200} deferredValidationTime={200}
id="confirmCollectionId" id="confirmCollectionId"
onChange={[Function]} onChange={[Function]}
@ -648,18 +649,18 @@ exports[`Delete Collection Confirmation Pane submit() should call delete collect
validateOnLoad={true} validateOnLoad={true}
> >
<div <div
className="ms-TextField root-112" className="ms-TextField root-144"
> >
<div <div
className="ms-TextField-wrapper" className="ms-TextField-wrapper"
> >
<div <div
className="ms-TextField-fieldGroup fieldGroup-113" className="ms-TextField-fieldGroup fieldGroup-145"
> >
<input <input
aria-invalid={false} aria-invalid={false}
autoFocus={true} autoFocus={true}
className="ms-TextField-field field-114" className="ms-TextField-field field-146"
id="confirmCollectionId" id="confirmCollectionId"
onBlur={[Function]} onBlur={[Function]}
onChange={[Function]} onChange={[Function]}
@ -682,7 +683,7 @@ exports[`Delete Collection Confirmation Pane submit() should call delete collect
variant="small" variant="small"
> >
<span <span
className="css-120" className="css-155"
> >
Help us improve Azure Cosmos DB! Help us improve Azure Cosmos DB!
</span> </span>
@ -692,7 +693,7 @@ exports[`Delete Collection Confirmation Pane submit() should call delete collect
variant="small" variant="small"
> >
<span <span
className="css-120" className="css-155"
> >
What is the reason why you are deleting this container? What is the reason why you are deleting this container?
</span> </span>
@ -711,6 +712,7 @@ exports[`Delete Collection Confirmation Pane submit() should call delete collect
} }
> >
<TextFieldBase <TextFieldBase
canRevealPassword={false}
deferredValidationTime={200} deferredValidationTime={200}
id="deleteCollectionFeedbackInput" id="deleteCollectionFeedbackInput"
multiline={true} multiline={true}
@ -994,17 +996,17 @@ exports[`Delete Collection Confirmation Pane submit() should call delete collect
validateOnLoad={true} validateOnLoad={true}
> >
<div <div
className="ms-TextField ms-TextField--multiline root-112" className="ms-TextField ms-TextField--multiline root-144"
> >
<div <div
className="ms-TextField-wrapper" className="ms-TextField-wrapper"
> >
<div <div
className="ms-TextField-fieldGroup fieldGroup-121" className="ms-TextField-fieldGroup fieldGroup-156"
> >
<textarea <textarea
aria-invalid={false} aria-invalid={false}
className="ms-TextField-field field-122" className="ms-TextField-field field-157"
id="deleteCollectionFeedbackInput" id="deleteCollectionFeedbackInput"
onBlur={[Function]} onBlur={[Function]}
onChange={[Function]} onChange={[Function]}
@ -1900,7 +1902,7 @@ exports[`Delete Collection Confirmation Pane submit() should call delete collect
"iconDisabled": Object { "iconDisabled": Object {
"color": "#a19f9d", "color": "#a19f9d",
"selectors": Object { "selectors": Object {
"@media screen and (-ms-high-contrast: active)": Object { "@media screen and (-ms-high-contrast: active), (forced-colors: active)": Object {
"color": "GrayText", "color": "GrayText",
}, },
}, },
@ -1926,7 +1928,7 @@ exports[`Delete Collection Confirmation Pane submit() should call delete collect
"menuIconDisabled": Object { "menuIconDisabled": Object {
"color": "#a19f9d", "color": "#a19f9d",
"selectors": Object { "selectors": Object {
"@media screen and (-ms-high-contrast: active)": Object { "@media screen and (-ms-high-contrast: active), (forced-colors: active)": Object {
"color": "GrayText", "color": "GrayText",
}, },
}, },
@ -1945,7 +1947,7 @@ exports[`Delete Collection Confirmation Pane submit() should call delete collect
"position": "absolute", "position": "absolute",
"right": 2, "right": 2,
"selectors": Object { "selectors": Object {
"@media screen and (-ms-high-contrast: active)": Object { "@media screen and (-ms-high-contrast: active), (forced-colors: active)": Object {
"bottom": -2, "bottom": -2,
"left": -2, "left": -2,
"outlineColor": "ButtonText", "outlineColor": "ButtonText",
@ -2003,11 +2005,12 @@ exports[`Delete Collection Confirmation Pane submit() should call delete collect
}, },
}, },
}, },
"@media screen and (-ms-high-contrast: active)": Object { "@media screen and (-ms-high-contrast: active), (forced-colors: active)": Object {
"MsHighContrastAdjust": "none", "MsHighContrastAdjust": "none",
"backgroundColor": "WindowText", "backgroundColor": "WindowText",
"borderColor": "WindowText", "borderColor": "WindowText",
"color": "Window", "color": "Window",
"forcedColorAdjust": "none",
}, },
}, },
}, },
@ -2034,7 +2037,7 @@ exports[`Delete Collection Confirmation Pane submit() should call delete collect
"position": "absolute", "position": "absolute",
"right": 2, "right": 2,
"selectors": Object { "selectors": Object {
"@media screen and (-ms-high-contrast: active)": Object { "@media screen and (-ms-high-contrast: active), (forced-colors: active)": Object {
"bottom": -2, "bottom": -2,
"left": -2, "left": -2,
"outlineColor": "ButtonText", "outlineColor": "ButtonText",
@ -2066,8 +2069,10 @@ exports[`Delete Collection Confirmation Pane submit() should call delete collect
}, },
}, },
Object { Object {
"backgroundColor": "#f3f2f1",
"color": "#d2d0ce",
"selectors": Object { "selectors": Object {
"@media screen and (-ms-high-contrast: active)": Object { "@media screen and (-ms-high-contrast: active), (forced-colors: active)": Object {
"backgroundColor": "Window", "backgroundColor": "Window",
"borderColor": "GrayText", "borderColor": "GrayText",
"color": "GrayText", "color": "GrayText",
@ -2084,7 +2089,7 @@ exports[`Delete Collection Confirmation Pane submit() should call delete collect
"border": "1px solid #106ebe", "border": "1px solid #106ebe",
"color": "#ffffff", "color": "#ffffff",
"selectors": Object { "selectors": Object {
"@media screen and (-ms-high-contrast: active)": Object { "@media screen and (-ms-high-contrast: active), (forced-colors: active)": Object {
"backgroundColor": "Highlight", "backgroundColor": "Highlight",
"borderColor": "Highlight", "borderColor": "Highlight",
"color": "Window", "color": "Window",
@ -2096,11 +2101,12 @@ exports[`Delete Collection Confirmation Pane submit() should call delete collect
"border": "1px solid #005a9e", "border": "1px solid #005a9e",
"color": "#ffffff", "color": "#ffffff",
"selectors": Object { "selectors": Object {
"@media screen and (-ms-high-contrast: active)": Object { "@media screen and (-ms-high-contrast: active), (forced-colors: active)": Object {
"MsHighContrastAdjust": "none", "MsHighContrastAdjust": "none",
"backgroundColor": "WindowText", "backgroundColor": "WindowText",
"borderColor": "WindowText", "borderColor": "WindowText",
"color": "Window", "color": "Window",
"forcedColorAdjust": "none",
}, },
}, },
}, },
@ -2116,7 +2122,7 @@ exports[`Delete Collection Confirmation Pane submit() should call delete collect
"splitButtonContainer": Array [ "splitButtonContainer": Array [
Object { Object {
"selectors": Object { "selectors": Object {
"@media screen and (-ms-high-contrast: active)": Object { "@media screen and (-ms-high-contrast: active), (forced-colors: active)": Object {
"border": "none", "border": "none",
}, },
}, },
@ -2134,7 +2140,7 @@ exports[`Delete Collection Confirmation Pane submit() should call delete collect
"position": "absolute", "position": "absolute",
"right": 3, "right": 3,
"selectors": Object { "selectors": Object {
"@media screen and (-ms-high-contrast: active)": Object { "@media screen and (-ms-high-contrast: active), (forced-colors: active)": Object {
"border": "none", "border": "none",
"bottom": -2, "bottom": -2,
"left": -2, "left": -2,
@ -2163,19 +2169,20 @@ exports[`Delete Collection Confirmation Pane submit() should call delete collect
"borderBottomRightRadius": "0", "borderBottomRightRadius": "0",
"borderTopRightRadius": "0", "borderTopRightRadius": "0",
"selectors": Object { "selectors": Object {
"@media screen and (-ms-high-contrast: active)": Object { "@media screen and (-ms-high-contrast: active), (forced-colors: active)": Object {
"MsHighContrastAdjust": "none", "MsHighContrastAdjust": "none",
"backgroundColor": "Window", "backgroundColor": "Window",
"border": "1px solid WindowText", "border": "1px solid WindowText",
"borderRightWidth": "0", "borderRightWidth": "0",
"color": "WindowText", "color": "WindowText",
"forcedColorAdjust": "none",
}, },
}, },
}, },
".ms-Button--primary + .ms-Button": Object { ".ms-Button--primary + .ms-Button": Object {
"border": "none", "border": "none",
"selectors": Object { "selectors": Object {
"@media screen and (-ms-high-contrast: active)": Object { "@media screen and (-ms-high-contrast: active), (forced-colors: active)": Object {
"border": "1px solid WindowText", "border": "1px solid WindowText",
"borderLeftWidth": "0", "borderLeftWidth": "0",
}, },
@ -2188,10 +2195,11 @@ exports[`Delete Collection Confirmation Pane submit() should call delete collect
"selectors": Object { "selectors": Object {
".ms-Button--primary": Object { ".ms-Button--primary": Object {
"selectors": Object { "selectors": Object {
"@media screen and (-ms-high-contrast: active)": Object { "@media screen and (-ms-high-contrast: active), (forced-colors: active)": Object {
"MsHighContrastAdjust": "none", "MsHighContrastAdjust": "none",
"backgroundColor": "WindowText", "backgroundColor": "WindowText",
"color": "Window", "color": "Window",
"forcedColorAdjust": "none",
}, },
}, },
}, },
@ -2201,10 +2209,11 @@ exports[`Delete Collection Confirmation Pane submit() should call delete collect
"selectors": Object { "selectors": Object {
".ms-Button--primary": Object { ".ms-Button--primary": Object {
"selectors": Object { "selectors": Object {
"@media screen and (-ms-high-contrast: active)": Object { "@media screen and (-ms-high-contrast: active), (forced-colors: active)": Object {
"MsHighContrastAdjust": "none", "MsHighContrastAdjust": "none",
"backgroundColor": "WindowText", "backgroundColor": "WindowText",
"color": "Window", "color": "Window",
"forcedColorAdjust": "none",
}, },
}, },
}, },
@ -2214,12 +2223,11 @@ exports[`Delete Collection Confirmation Pane submit() should call delete collect
"border": "none", "border": "none",
"outline": "none", "outline": "none",
"selectors": Object { "selectors": Object {
"@media screen and (-ms-high-contrast: active)": Object { "@media screen and (-ms-high-contrast: active), (forced-colors: active)": Object {
"MsHighContrastAdjust": "none",
"backgroundColor": "Window", "backgroundColor": "Window",
"borderColor": "GrayText", "borderColor": "GrayText",
"color": "GrayText", "color": "GrayText",
},
"@media screen and (forced-colors: active)": Object {
"forcedColorAdjust": "none", "forcedColorAdjust": "none",
}, },
}, },
@ -2231,7 +2239,7 @@ exports[`Delete Collection Confirmation Pane submit() should call delete collect
"selectors": Object { "selectors": Object {
".ms-Button--primary": Object { ".ms-Button--primary": Object {
"selectors": Object { "selectors": Object {
"@media screen and (-ms-high-contrast: active)": Object { "@media screen and (-ms-high-contrast: active), (forced-colors: active)": Object {
"backgroundColor": "Highlight", "backgroundColor": "Highlight",
"color": "Window", "color": "Window",
}, },
@ -2240,7 +2248,7 @@ exports[`Delete Collection Confirmation Pane submit() should call delete collect
".ms-Button.is-disabled": Object { ".ms-Button.is-disabled": Object {
"color": "#a19f9d", "color": "#a19f9d",
"selectors": Object { "selectors": Object {
"@media screen and (-ms-high-contrast: active)": Object { "@media screen and (-ms-high-contrast: active), (forced-colors: active)": Object {
"backgroundColor": "Window", "backgroundColor": "Window",
"borderColor": "GrayText", "borderColor": "GrayText",
"color": "GrayText", "color": "GrayText",
@ -2256,7 +2264,7 @@ exports[`Delete Collection Confirmation Pane submit() should call delete collect
"position": "absolute", "position": "absolute",
"right": 31, "right": 31,
"selectors": Object { "selectors": Object {
"@media screen and (-ms-high-contrast: active)": Object { "@media screen and (-ms-high-contrast: active), (forced-colors: active)": Object {
"backgroundColor": "Window", "backgroundColor": "Window",
}, },
}, },
@ -2268,7 +2276,7 @@ exports[`Delete Collection Confirmation Pane submit() should call delete collect
"position": "absolute", "position": "absolute",
"right": 31, "right": 31,
"selectors": Object { "selectors": Object {
"@media screen and (-ms-high-contrast: active)": Object { "@media screen and (-ms-high-contrast: active), (forced-colors: active)": Object {
"backgroundColor": "WindowText", "backgroundColor": "WindowText",
}, },
}, },
@ -2281,7 +2289,7 @@ exports[`Delete Collection Confirmation Pane submit() should call delete collect
"position": "absolute", "position": "absolute",
"right": 31, "right": 31,
"selectors": Object { "selectors": Object {
"@media screen and (-ms-high-contrast: active)": Object { "@media screen and (-ms-high-contrast: active), (forced-colors: active)": Object {
"backgroundColor": "GrayText", "backgroundColor": "GrayText",
}, },
}, },
@ -2303,17 +2311,22 @@ exports[`Delete Collection Confirmation Pane submit() should call delete collect
":hover": Object { ":hover": Object {
"backgroundColor": "#106ebe", "backgroundColor": "#106ebe",
"selectors": Object { "selectors": Object {
"@media screen and (-ms-high-contrast: active)": Object { "@media screen and (-ms-high-contrast: active), (forced-colors: active)": Object {
"color": "Highlight", "color": "Highlight",
}, },
}, },
}, },
"@media screen and (-ms-high-contrast: active)": Object { "@media screen and (-ms-high-contrast: active), (forced-colors: active)": Object {
"backgroundColor": "WindowText", "backgroundColor": "WindowText",
}, },
}, },
}, },
Object { Object {
"@media screen and (-ms-high-contrast: active), (forced-colors: active)": Object {
".ms-Button-menuIcon": Object {
"color": "WindowText",
},
},
"border": "1px solid #8a8886", "border": "1px solid #8a8886",
"borderBottomRightRadius": "2px", "borderBottomRightRadius": "2px",
"borderLeft": "none", "borderLeft": "none",
@ -2359,7 +2372,7 @@ exports[`Delete Collection Confirmation Pane submit() should call delete collect
"selectors": Object { "selectors": Object {
".ms-Button--primary": Object { ".ms-Button--primary": Object {
"selectors": Object { "selectors": Object {
"@media screen and (-ms-high-contrast: active)": Object { "@media screen and (-ms-high-contrast: active), (forced-colors: active)": Object {
"backgroundColor": "Window", "backgroundColor": "Window",
"borderColor": "GrayText", "borderColor": "GrayText",
"color": "GrayText", "color": "GrayText",
@ -2368,7 +2381,7 @@ exports[`Delete Collection Confirmation Pane submit() should call delete collect
}, },
".ms-Button-menuIcon": Object { ".ms-Button-menuIcon": Object {
"selectors": Object { "selectors": Object {
"@media screen and (-ms-high-contrast: active)": Object { "@media screen and (-ms-high-contrast: active), (forced-colors: active)": Object {
"color": "GrayText", "color": "GrayText",
}, },
}, },
@ -2376,7 +2389,7 @@ exports[`Delete Collection Confirmation Pane submit() should call delete collect
":hover": Object { ":hover": Object {
"cursor": "default", "cursor": "default",
}, },
"@media screen and (-ms-high-contrast: active)": Object { "@media screen and (-ms-high-contrast: active), (forced-colors: active)": Object {
"backgroundColor": "Window", "backgroundColor": "Window",
"border": "1px solid GrayText", "border": "1px solid GrayText",
"color": "GrayText", "color": "GrayText",
@ -2398,7 +2411,7 @@ exports[`Delete Collection Confirmation Pane submit() should call delete collect
"splitButtonMenuIconDisabled": Object { "splitButtonMenuIconDisabled": Object {
"color": "#a19f9d", "color": "#a19f9d",
"selectors": Object { "selectors": Object {
"@media screen and (-ms-high-contrast: active)": Object { "@media screen and (-ms-high-contrast: active), (forced-colors: active)": Object {
"color": "GrayText", "color": "GrayText",
}, },
}, },
@ -2686,7 +2699,7 @@ exports[`Delete Collection Confirmation Pane submit() should call delete collect
variantClassName="ms-Button--primary" variantClassName="ms-Button--primary"
> >
<button <button
className="ms-Button ms-Button--primary root-124" className="ms-Button ms-Button--primary root-159"
data-is-focusable={true} data-is-focusable={true}
id="sidePanelOkButton" id="sidePanelOkButton"
onClick={[Function]} onClick={[Function]}
@ -2698,14 +2711,14 @@ exports[`Delete Collection Confirmation Pane submit() should call delete collect
type="button" type="button"
> >
<span <span
className="ms-Button-flexContainer flexContainer-125" className="ms-Button-flexContainer flexContainer-160"
data-automationid="splitbuttonprimary" data-automationid="splitbuttonprimary"
> >
<span <span
className="ms-Button-textContainer textContainer-126" className="ms-Button-textContainer textContainer-161"
> >
<span <span
className="ms-Button-label label-128" className="ms-Button-label label-163"
id="id__6" id="id__6"
key="id__6" key="id__6"
> >

View File

@ -27,8 +27,8 @@
"Enable DB level throughput": "Enable Database Level Throughput", "Enable DB level throughput": "Enable Database Level Throughput",
"Database Throughput": "Database Throughput", "Database Throughput": "Database Throughput",
"UpdateInProgressMessage": "Data is being updated", "UpdateInProgressMessage": "Data is being updated",
"UpdateCompletedMessageTitle":"Update succeeded", "UpdateCompletedMessageTitle": "Update succeeded",
"UpdateCompletedMessageText": "Data updation completed.", "UpdateCompletedMessageText": "Data update completed.",
"SubmissionMessageSuccessTitle": "Update started", "SubmissionMessageSuccessTitle": "Update started",
"SubmissionMessageForNewRegionText": "Data update started. Region changed.", "SubmissionMessageForNewRegionText": "Data update started. Region changed.",
"SubmissionMessageForSameRegionText": "Data update started. Region not changed.", "SubmissionMessageForSameRegionText": "Data update started. Region not changed.",
@ -36,7 +36,6 @@
"SubmissionMessageErrorText": "Data update failed because of errors.", "SubmissionMessageErrorText": "Data update failed because of errors.",
"OnSaveFailureMessage": "Data save operation not currently permitted." "OnSaveFailureMessage": "Data save operation not currently permitted."
}, },
"SqlX": { "SqlX": {}
}
} }
} }

View File

@ -1,4 +1,4 @@
import { PropertyInfo, OnChange, Values, IsDisplayable, RefreshOptions } from "../Decorators"; import { IsDisplayable, OnChange, PropertyInfo, RefreshOptions, Values } from "../Decorators";
import { import {
ChoiceItem, ChoiceItem,
Description, Description,
@ -12,14 +12,14 @@ import {
SmartUiInput, SmartUiInput,
} from "../SelfServeTypes"; } from "../SelfServeTypes";
import { import {
getMaxCollectionThroughput,
getMaxDatabaseThroughput,
getMinCollectionThroughput,
getMinDatabaseThroughput,
initialize,
onRefreshSelfServeExample, onRefreshSelfServeExample,
Regions, Regions,
update, update,
initialize,
getMinDatabaseThroughput,
getMaxDatabaseThroughput,
getMinCollectionThroughput,
getMaxCollectionThroughput,
} from "./SelfServeExample.rp"; } from "./SelfServeExample.rp";
const regionDropdownItems: ChoiceItem[] = [ const regionDropdownItems: ChoiceItem[] = [
@ -203,11 +203,7 @@ export default class SelfServeExample extends SelfServeBaseClass {
public initialize = async (): Promise<Map<string, SmartUiInput>> => { public initialize = async (): Promise<Map<string, SmartUiInput>> => {
const initializeResponse = await initialize(); const initializeResponse = await initialize();
const defaults = new Map<string, SmartUiInput>(); const defaults = new Map<string, SmartUiInput>();
const currentRegionText = `current region selected is ${initializeResponse.regions}`; defaults.set("currentRegionText", undefined);
defaults.set("currentRegionText", {
value: { textTKey: currentRegionText, type: DescriptionType.Text } as Description,
hidden: false,
});
defaults.set("regions", { value: initializeResponse.regions }); defaults.set("regions", { value: initializeResponse.regions });
defaults.set("enableLogging", { value: initializeResponse.enableLogging }); defaults.set("enableLogging", { value: initializeResponse.enableLogging });
const accountName = initializeResponse.accountName; 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 * as React from "react";
import ReactDOM from "react-dom"; import ReactDOM from "react-dom";
import { normalizeArmEndpoint } from "../Common/EnvironmentUtility";
import { sendMessage } from "../Common/MessageHandler"; 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 { isInvalidParentFrameOrigin } from "../Utils/MessageValidation";
import "./SelfServe.less";
import { SelfServeComponent } from "./SelfServeComponent"; import { SelfServeComponent } from "./SelfServeComponent";
import { SelfServeDescriptor } from "./SelfServeTypes"; import { SelfServeDescriptor } from "./SelfServeTypes";
import { SelfServeType } from "./SelfServeUtils"; 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(); initializeIcons();
const getDescriptor = async (selfServeType: SelfServeType): Promise<SelfServeDescriptor> => { const getDescriptor = async (selfServeType: SelfServeType): Promise<SelfServeDescriptor> => {

View File

@ -1,34 +1,36 @@
import React from "react"; import { TFunction } from "i18next";
import { import {
CommandBar, CommandBar,
ICommandBarItemProps, ICommandBarItemProps,
IStackTokens,
MessageBar, MessageBar,
MessageBarType, MessageBarType,
Separator,
Spinner, Spinner,
SpinnerSize, SpinnerSize,
Stack, Stack,
} from "office-ui-fabric-react"; } 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 { import {
AnyDisplay, AnyDisplay,
Node, BooleanInput,
ChoiceInput,
DescriptionDisplay,
InputType, InputType,
Node,
NumberInput,
RefreshResult, RefreshResult,
SelfServeDescriptor, SelfServeDescriptor,
SmartUiInput, SmartUiInput,
DescriptionDisplay,
StringInput, StringInput,
NumberInput,
BooleanInput,
ChoiceInput,
} from "./SelfServeTypes"; } 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 { interface SelfServeNotification {
message: string; message: string;
@ -127,7 +129,7 @@ export class SelfServeComponent extends React.Component<SelfServeComponentProps,
this.props.descriptor.inputNames.map((inputName) => { this.props.descriptor.inputNames.map((inputName) => {
let initialValue = initialValues.get(inputName); let initialValue = initialValues.get(inputName);
if (!initialValue) { if (!initialValue) {
initialValue = { value: undefined, hidden: false }; initialValue = { value: undefined, hidden: false, disabled: false };
} }
currentValues = currentValues.set(inputName, initialValue); currentValues = currentValues.set(inputName, initialValue);
baselineValues = baselineValues.set(inputName, initialValue); baselineValues = baselineValues.set(inputName, initialValue);
@ -311,34 +313,41 @@ export class SelfServeComponent extends React.Component<SelfServeComponentProps,
this.performSave(); this.performSave();
}; };
public isDiscardButtonDisabled = (): boolean => { public isInputModified = (): boolean => {
if (this.state.isSaving) {
return true;
}
for (const key of this.state.currentValues.keys()) { for (const key of this.state.currentValues.keys()) {
const currentValue = JSON.stringify(this.state.currentValues.get(key)); const currentValue = this.state.currentValues.get(key);
const baselineValue = JSON.stringify(this.state.baselineValues.get(key)); if (currentValue && currentValue.hidden === undefined) {
currentValue.hidden = false;
}
if (currentValue && currentValue.disabled === undefined) {
currentValue.disabled = false;
}
if (currentValue !== baselineValue) { const baselineValue = this.state.baselineValues.get(key);
return false; 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 => { public isSaveButtonDisabled = (): boolean => {
if (this.state.hasErrors || this.state.isSaving) { return this.state.hasErrors || this.isRefreshing() || !this.isInputModified();
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;
}; };
private performRefresh = async (): Promise<void> => { private performRefresh = async (): Promise<void> => {
@ -397,7 +406,6 @@ export class SelfServeComponent extends React.Component<SelfServeComponentProps,
key: "save", key: "save",
text: this.getCommonTranslation("Save"), text: this.getCommonTranslation("Save"),
iconProps: { iconName: "Save" }, iconProps: { iconName: "Save" },
split: true,
disabled: this.isSaveButtonDisabled(), disabled: this.isSaveButtonDisabled(),
onClick: () => this.onSaveButtonClick(), onClick: () => this.onSaveButtonClick(),
}, },
@ -405,21 +413,21 @@ export class SelfServeComponent extends React.Component<SelfServeComponentProps,
key: "discard", key: "discard",
text: this.getCommonTranslation("Discard"), text: this.getCommonTranslation("Discard"),
iconProps: { iconName: "Undo" }, iconProps: { iconName: "Undo" },
split: true,
disabled: this.isDiscardButtonDisabled(), disabled: this.isDiscardButtonDisabled(),
onClick: () => { onClick: () => {
this.discard(); this.discard();
}, },
buttonStyles: commandBarItemStyles,
}, },
{ {
key: "refresh", key: "refresh",
text: this.getCommonTranslation("Refresh"), text: this.getCommonTranslation("Refresh"),
disabled: this.state.isInitializing, disabled: this.state.isInitializing,
iconProps: { iconName: "Refresh" }, iconProps: { iconName: "Refresh" },
split: true,
onClick: () => { onClick: () => {
this.onRefreshClicked(); this.onRefreshClicked();
}, },
buttonStyles: commandBarItemStyles,
}, },
]; ];
}; };
@ -432,7 +440,6 @@ export class SelfServeComponent extends React.Component<SelfServeComponentProps,
}; };
public render(): JSX.Element { public render(): JSX.Element {
const containerStackTokens: IStackTokens = { childrenGap: 5 };
if (this.state.compileErrorMessage) { if (this.state.compileErrorMessage) {
return <MessageBar messageBarType={MessageBarType.error}>{this.state.compileErrorMessage}</MessageBar>; return <MessageBar messageBarType={MessageBarType.error}>{this.state.compileErrorMessage}</MessageBar>;
} }
@ -445,13 +452,13 @@ export class SelfServeComponent extends React.Component<SelfServeComponentProps,
return ( return (
<div style={{ overflowX: "auto" }}> <div style={{ overflowX: "auto" }}>
<Stack tokens={containerStackTokens} styles={{ root: { padding: 10 } }}> <Stack tokens={containerStackTokens}>
<CommandBar styles={{ root: { paddingLeft: 0 } }} items={this.getCommandBarItems()} /> <Stack.Item>
<CommandBar styles={commandBarStyles} items={this.getCommandBarItems()} />
<Separator styles={separatorStyles} />
</Stack.Item>
{this.state.isInitializing ? ( {this.state.isInitializing ? (
<Spinner <Spinner size={SpinnerSize.large} />
size={SpinnerSize.large}
styles={{ root: { textAlign: "center", justifyContent: "center", width: "100%", height: "100%" } }}
/>
) : ( ) : (
<> <>
{this.state.notification && ( {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 "reflect-metadata";
import { userContext } from "../UserContext";
import { import {
Node,
AnyDisplay, AnyDisplay,
BooleanInput, BooleanInput,
ChoiceInput, ChoiceInput,
@ -10,13 +10,13 @@ import {
Info, Info,
InputType, InputType,
InputTypeValue, InputTypeValue,
Node,
NumberInput, NumberInput,
RefreshParams,
SelfServeDescriptor, SelfServeDescriptor,
SmartUiInput, SmartUiInput,
StringInput, StringInput,
RefreshParams,
} from "./SelfServeTypes"; } from "./SelfServeTypes";
import { userContext } from "../UserContext";
export enum SelfServeType { export enum SelfServeType {
// No self serve type passed, launch explorer // No self serve type passed, launch explorer
@ -195,5 +195,5 @@ export const generateBladeLink = (blade: BladeType): string => {
const subscriptionId = userContext.subscriptionId; const subscriptionId = userContext.subscriptionId;
const resourceGroupName = userContext.resourceGroup; const resourceGroupName = userContext.resourceGroup;
const databaseAccountName = userContext.databaseAccount.name; 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}`;
}; };

View File

@ -12,14 +12,19 @@ describe("Self Serve", () => {
// id of the display element is in the format {PROPERTY_NAME}-{DISPLAY_NAME}-{DISPLAY_TYPE} // id of the display element is in the format {PROPERTY_NAME}-{DISPLAY_NAME}-{DISPLAY_TYPE}
await frame.waitForSelector("#description-text-display"); await frame.waitForSelector("#description-text-display");
await frame.waitForSelector("#currentRegionText-text-display");
const regions = await frame.waitForSelector("#regions-dropdown-input"); const regions = await frame.waitForSelector("#regions-dropdown-input");
const currentRegionsDescription = await frame.$$("#currentRegionText-text-display");
expect(currentRegionsDescription).toHaveLength(0);
let disabledLoggingToggle = await frame.$$("#enableLogging-toggle-input[disabled]"); let disabledLoggingToggle = await frame.$$("#enableLogging-toggle-input[disabled]");
expect(disabledLoggingToggle).toHaveLength(0); expect(disabledLoggingToggle).toHaveLength(0);
await regions.click(); await regions.click();
const regionsDropdownElement1 = await frame.waitForSelector("#regions-dropdown-input-list0"); const regionsDropdownElement1 = await frame.waitForSelector("#regions-dropdown-input-list0");
await regionsDropdownElement1.click(); await regionsDropdownElement1.click();
await frame.waitForSelector("#currentRegionText-text-display");
disabledLoggingToggle = await frame.$$("#enableLogging-toggle-input[disabled]"); disabledLoggingToggle = await frame.$$("#enableLogging-toggle-input[disabled]");
expect(disabledLoggingToggle).toHaveLength(1); expect(disabledLoggingToggle).toHaveLength(1);