mirror of
https://github.com/Azure/cosmos-explorer.git
synced 2025-12-25 11:51:07 +00:00
Compare commits
8 Commits
all-checks
...
tsStrict/f
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
17fa6aff31 | ||
|
|
c42a10faa5 | ||
|
|
0d79f01304 | ||
|
|
eae5b2219e | ||
|
|
2fda881770 | ||
|
|
35f8fa8324 | ||
|
|
0e413430dc | ||
|
|
afd7f43eb8 |
@@ -32,7 +32,7 @@ export const EntityValue: FunctionComponent<TableEntityProps> = ({
|
|||||||
<DatePicker
|
<DatePicker
|
||||||
className="addEntityDatePicker"
|
className="addEntityDatePicker"
|
||||||
placeholder={entityValuePlaceholder}
|
placeholder={entityValuePlaceholder}
|
||||||
value={entityValue && new Date(entityValue)}
|
value={entityValue ? new Date(entityValue) : new Date()}
|
||||||
ariaLabel={entityValuePlaceholder}
|
ariaLabel={entityValuePlaceholder}
|
||||||
onSelectDate={onSelectDate}
|
onSelectDate={onSelectDate}
|
||||||
disabled={isEntityValueDisable}
|
disabled={isEntityValueDisable}
|
||||||
@@ -59,7 +59,7 @@ export const EntityValue: FunctionComponent<TableEntityProps> = ({
|
|||||||
disabled={isEntityValueDisable}
|
disabled={isEntityValueDisable}
|
||||||
type={entityValueType}
|
type={entityValueType}
|
||||||
placeholder={entityValuePlaceholder}
|
placeholder={entityValuePlaceholder}
|
||||||
value={typeof entityValue === "string" && entityValue}
|
value={typeof entityValue === "string" ? entityValue : ""}
|
||||||
onChange={onEntityValueChange}
|
onChange={onEntityValueChange}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -1,105 +0,0 @@
|
|||||||
import React from "react";
|
|
||||||
import { shallow, mount } from "enzyme";
|
|
||||||
import { DefaultDirectoryDropdownComponent, DefaultDirectoryDropdownProps } from "./DefaultDirectoryDropdownComponent";
|
|
||||||
import { Tenant } from "../../../Contracts/DataModels";
|
|
||||||
|
|
||||||
const createBlankProps = (): DefaultDirectoryDropdownProps => {
|
|
||||||
return {
|
|
||||||
defaultDirectoryId: "",
|
|
||||||
directories: [],
|
|
||||||
onDefaultDirectoryChange: jest.fn(),
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
const createBlankDirectory = (): Tenant => {
|
|
||||||
return {
|
|
||||||
countryCode: "",
|
|
||||||
displayName: "",
|
|
||||||
domains: [],
|
|
||||||
id: "",
|
|
||||||
tenantId: "",
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
describe("test render", () => {
|
|
||||||
it("renders with no directories", () => {
|
|
||||||
const props = createBlankProps();
|
|
||||||
|
|
||||||
const wrapper = shallow(<DefaultDirectoryDropdownComponent {...props} />);
|
|
||||||
expect(wrapper).toMatchSnapshot();
|
|
||||||
});
|
|
||||||
|
|
||||||
it("renders with directories but no default", () => {
|
|
||||||
const props = createBlankProps();
|
|
||||||
const tenant1 = createBlankDirectory();
|
|
||||||
tenant1.displayName = "Microsoft";
|
|
||||||
tenant1.tenantId = "asdfghjklzxcvbnm1234567890";
|
|
||||||
const tenant2 = createBlankDirectory();
|
|
||||||
tenant1.displayName = "Macrohard";
|
|
||||||
tenant1.tenantId = "asdfghjklzxcvbnm9876543210";
|
|
||||||
props.directories = [tenant1, tenant2];
|
|
||||||
|
|
||||||
const wrapper = shallow(<DefaultDirectoryDropdownComponent {...props} />);
|
|
||||||
expect(wrapper).toMatchSnapshot();
|
|
||||||
});
|
|
||||||
|
|
||||||
it("renders with directories and default", () => {
|
|
||||||
const props = createBlankProps();
|
|
||||||
const tenant1 = createBlankDirectory();
|
|
||||||
tenant1.displayName = "Microsoft";
|
|
||||||
tenant1.tenantId = "asdfghjklzxcvbnm1234567890";
|
|
||||||
const tenant2 = createBlankDirectory();
|
|
||||||
tenant1.displayName = "Macrohard";
|
|
||||||
tenant1.tenantId = "asdfghjklzxcvbnm9876543210";
|
|
||||||
props.directories = [tenant1, tenant2];
|
|
||||||
|
|
||||||
props.defaultDirectoryId = "asdfghjklzxcvbnm9876543210";
|
|
||||||
|
|
||||||
const wrapper = shallow(<DefaultDirectoryDropdownComponent {...props} />);
|
|
||||||
expect(wrapper).toMatchSnapshot();
|
|
||||||
});
|
|
||||||
|
|
||||||
it("renders with directories and last visit default", () => {
|
|
||||||
const props = createBlankProps();
|
|
||||||
const tenant1 = createBlankDirectory();
|
|
||||||
tenant1.displayName = "Microsoft";
|
|
||||||
tenant1.tenantId = "asdfghjklzxcvbnm1234567890";
|
|
||||||
const tenant2 = createBlankDirectory();
|
|
||||||
tenant1.displayName = "Macrohard";
|
|
||||||
tenant1.tenantId = "asdfghjklzxcvbnm9876543210";
|
|
||||||
props.directories = [tenant1, tenant2];
|
|
||||||
|
|
||||||
props.defaultDirectoryId = "lastVisited";
|
|
||||||
|
|
||||||
const wrapper = shallow(<DefaultDirectoryDropdownComponent {...props} />);
|
|
||||||
expect(wrapper).toMatchSnapshot();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe("test function", () => {
|
|
||||||
it("on default directory change", () => {
|
|
||||||
const props = createBlankProps();
|
|
||||||
const tenant1 = createBlankDirectory();
|
|
||||||
tenant1.displayName = "Microsoft";
|
|
||||||
tenant1.tenantId = "asdfghjklzxcvbnm1234567890";
|
|
||||||
const tenant2 = createBlankDirectory();
|
|
||||||
tenant1.displayName = "Macrohard";
|
|
||||||
tenant1.tenantId = "asdfghjklzxcvbnm9876543210";
|
|
||||||
props.directories = [tenant1, tenant2];
|
|
||||||
props.defaultDirectoryId = "lastVisited";
|
|
||||||
|
|
||||||
const wrapper = mount(<DefaultDirectoryDropdownComponent {...props} />);
|
|
||||||
|
|
||||||
wrapper.find("div.defaultDirectoryDropdown").find("div.ms-Dropdown").simulate("click");
|
|
||||||
expect(wrapper.exists("div.ms-Callout-main")).toBe(true);
|
|
||||||
wrapper.find("button.ms-Dropdown-item").at(1).simulate("click");
|
|
||||||
expect(props.onDefaultDirectoryChange).toBeCalled();
|
|
||||||
expect(props.onDefaultDirectoryChange).toHaveBeenCalled();
|
|
||||||
|
|
||||||
wrapper.find("div.defaultDirectoryDropdown").find("div.ms-Dropdown").simulate("click");
|
|
||||||
expect(wrapper.exists("div.ms-Callout-main")).toBe(true);
|
|
||||||
wrapper.find("button.ms-Dropdown-item").at(0).simulate("click");
|
|
||||||
expect(props.onDefaultDirectoryChange).toBeCalled();
|
|
||||||
expect(props.onDefaultDirectoryChange).toHaveBeenCalled();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
@@ -1,71 +0,0 @@
|
|||||||
/**
|
|
||||||
* React component for Switch Directory
|
|
||||||
*/
|
|
||||||
|
|
||||||
import { Dropdown, IDropdownOption, IDropdownProps } from "@fluentui/react";
|
|
||||||
import * as React from "react";
|
|
||||||
import _ from "underscore";
|
|
||||||
import { Tenant } from "../../../Contracts/DataModels";
|
|
||||||
|
|
||||||
export interface DefaultDirectoryDropdownProps {
|
|
||||||
directories: Array<Tenant>;
|
|
||||||
defaultDirectoryId: string;
|
|
||||||
onDefaultDirectoryChange: (newDirectory: Tenant) => void;
|
|
||||||
}
|
|
||||||
|
|
||||||
export class DefaultDirectoryDropdownComponent extends React.Component<DefaultDirectoryDropdownProps> {
|
|
||||||
public static readonly lastVisitedKey: string = "lastVisited";
|
|
||||||
|
|
||||||
public render(): JSX.Element {
|
|
||||||
const lastVisitedOption: IDropdownOption = {
|
|
||||||
key: DefaultDirectoryDropdownComponent.lastVisitedKey,
|
|
||||||
text: "Sign in to your last visited directory",
|
|
||||||
};
|
|
||||||
const directoryOptions: Array<IDropdownOption> = this.props.directories.map(
|
|
||||||
(dirc): IDropdownOption => {
|
|
||||||
return {
|
|
||||||
key: dirc.tenantId,
|
|
||||||
text: `${dirc.displayName}(${dirc.tenantId})`,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
);
|
|
||||||
const dropDownOptions: Array<IDropdownOption> = [lastVisitedOption, ...directoryOptions];
|
|
||||||
const dropDownProps: IDropdownProps = {
|
|
||||||
label: "Set your default directory",
|
|
||||||
options: dropDownOptions,
|
|
||||||
defaultSelectedKey: this.props.defaultDirectoryId ? this.props.defaultDirectoryId : lastVisitedOption.key,
|
|
||||||
onChange: this._onDropdownChange,
|
|
||||||
className: "defaultDirectoryDropdown",
|
|
||||||
};
|
|
||||||
|
|
||||||
return <Dropdown {...dropDownProps} />;
|
|
||||||
}
|
|
||||||
|
|
||||||
private _onDropdownChange = (e: React.FormEvent<HTMLDivElement>, option?: IDropdownOption, index?: number): void => {
|
|
||||||
if (!option || !option.key) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (option.key === this.props.defaultDirectoryId) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (option.key === DefaultDirectoryDropdownComponent.lastVisitedKey) {
|
|
||||||
this.props.onDefaultDirectoryChange({
|
|
||||||
tenantId: option.key,
|
|
||||||
countryCode: undefined,
|
|
||||||
displayName: undefined,
|
|
||||||
domains: [],
|
|
||||||
id: undefined,
|
|
||||||
});
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const selectedDirectory = _.find(this.props.directories, (d) => d.tenantId === option.key);
|
|
||||||
if (!selectedDirectory) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.props.onDefaultDirectoryChange(selectedDirectory);
|
|
||||||
};
|
|
||||||
}
|
|
||||||
@@ -1,36 +0,0 @@
|
|||||||
import * as ko from "knockout";
|
|
||||||
import * as React from "react";
|
|
||||||
import { DirectoryListComponent, DirectoryListProps } from "./DirectoryListComponent";
|
|
||||||
import { DefaultDirectoryDropdownComponent, DefaultDirectoryDropdownProps } from "./DefaultDirectoryDropdownComponent";
|
|
||||||
import { ReactAdapter } from "../../../Bindings/ReactBindingHandler";
|
|
||||||
|
|
||||||
export class DirectoryComponentAdapter implements ReactAdapter {
|
|
||||||
public parameters: ko.Observable<number>;
|
|
||||||
|
|
||||||
constructor(
|
|
||||||
private _dropdownProps: ko.Observable<DefaultDirectoryDropdownProps>,
|
|
||||||
private _listProps: ko.Observable<DirectoryListProps>
|
|
||||||
) {
|
|
||||||
this._dropdownProps.subscribe(() => this.forceRender());
|
|
||||||
this._listProps.subscribe(() => this.forceRender());
|
|
||||||
this.parameters = ko.observable<number>(Date.now());
|
|
||||||
}
|
|
||||||
|
|
||||||
public renderComponent(): JSX.Element {
|
|
||||||
return (
|
|
||||||
<div>
|
|
||||||
<div className="directoryDropdownContainer">
|
|
||||||
<DefaultDirectoryDropdownComponent {...this._dropdownProps()} />
|
|
||||||
</div>
|
|
||||||
<div className="directoryDivider" />
|
|
||||||
<div className="directoryListContainer">
|
|
||||||
<DirectoryListComponent {...this._listProps()} />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
public forceRender(): void {
|
|
||||||
window.requestAnimationFrame(() => this.parameters(Date.now()));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,78 +0,0 @@
|
|||||||
import React from "react";
|
|
||||||
import { shallow, mount } from "enzyme";
|
|
||||||
import { DirectoryListComponent, DirectoryListProps } from "./DirectoryListComponent";
|
|
||||||
import { Tenant } from "../../../Contracts/DataModels";
|
|
||||||
|
|
||||||
const createBlankProps = (): DirectoryListProps => {
|
|
||||||
return {
|
|
||||||
selectedDirectoryId: undefined,
|
|
||||||
directories: [],
|
|
||||||
onNewDirectorySelected: jest.fn(),
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
const createBlankDirectory = (): Tenant => {
|
|
||||||
return {
|
|
||||||
countryCode: undefined,
|
|
||||||
displayName: undefined,
|
|
||||||
domains: [],
|
|
||||||
id: undefined,
|
|
||||||
tenantId: undefined,
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
describe("test render", () => {
|
|
||||||
it("renders with no directories", () => {
|
|
||||||
const props = createBlankProps();
|
|
||||||
|
|
||||||
const wrapper = shallow(<DirectoryListComponent {...props} />);
|
|
||||||
expect(wrapper).toMatchSnapshot();
|
|
||||||
});
|
|
||||||
|
|
||||||
it("renders with directories and selected", () => {
|
|
||||||
const props = createBlankProps();
|
|
||||||
const tenant1 = createBlankDirectory();
|
|
||||||
tenant1.displayName = "Microsoft";
|
|
||||||
tenant1.tenantId = "asdfghjklzxcvbnm1234567890";
|
|
||||||
const tenant2 = createBlankDirectory();
|
|
||||||
tenant1.displayName = "Macrohard";
|
|
||||||
tenant1.tenantId = "asdfghjklzxcvbnm9876543210";
|
|
||||||
props.directories = [tenant1, tenant2];
|
|
||||||
|
|
||||||
props.selectedDirectoryId = "asdfghjklzxcvbnm9876543210";
|
|
||||||
|
|
||||||
const wrapper = shallow(<DirectoryListComponent {...props} />);
|
|
||||||
expect(wrapper).toMatchSnapshot();
|
|
||||||
});
|
|
||||||
|
|
||||||
it("renders with filters", () => {
|
|
||||||
const props = createBlankProps();
|
|
||||||
const tenant1 = createBlankDirectory();
|
|
||||||
tenant1.displayName = "Microsoft";
|
|
||||||
tenant1.tenantId = "1234567890";
|
|
||||||
const tenant2 = createBlankDirectory();
|
|
||||||
tenant1.displayName = "Macrohard";
|
|
||||||
tenant1.tenantId = "9876543210";
|
|
||||||
props.directories = [tenant1, tenant2];
|
|
||||||
props.selectedDirectoryId = "9876543210";
|
|
||||||
|
|
||||||
const wrapper = mount(<DirectoryListComponent {...props} />);
|
|
||||||
wrapper.find("input.ms-TextField-field").simulate("change", { target: { value: "Macro" } });
|
|
||||||
expect(wrapper).toMatchSnapshot();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe("test function", () => {
|
|
||||||
it("on new directory selected", () => {
|
|
||||||
const props = createBlankProps();
|
|
||||||
const tenant1 = createBlankDirectory();
|
|
||||||
tenant1.displayName = "Microsoft";
|
|
||||||
tenant1.tenantId = "asdfghjklzxcvbnm1234567890";
|
|
||||||
props.directories = [tenant1];
|
|
||||||
|
|
||||||
const wrapper = mount(<DirectoryListComponent {...props} />);
|
|
||||||
wrapper.find("button.directoryListButton").simulate("click");
|
|
||||||
expect(props.onNewDirectorySelected).toBeCalled();
|
|
||||||
expect(props.onNewDirectorySelected).toHaveBeenCalled();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
@@ -1,126 +0,0 @@
|
|||||||
import {
|
|
||||||
DefaultButton,
|
|
||||||
IButtonProps,
|
|
||||||
ITextFieldProps,
|
|
||||||
List,
|
|
||||||
ScrollablePane,
|
|
||||||
Sticky,
|
|
||||||
StickyPositionType,
|
|
||||||
TextField,
|
|
||||||
} from "@fluentui/react";
|
|
||||||
import * as React from "react";
|
|
||||||
import _ from "underscore";
|
|
||||||
import { Tenant } from "../../../Contracts/DataModels";
|
|
||||||
|
|
||||||
export interface DirectoryListProps {
|
|
||||||
directories: Array<Tenant>;
|
|
||||||
selectedDirectoryId: string;
|
|
||||||
onNewDirectorySelected: (newDirectory: Tenant) => void;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface DirectoryListComponentState {
|
|
||||||
filterText: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
// onRenderCell is not called when selectedDirectoryId changed, so add a selected state to force render
|
|
||||||
interface ListTenant extends Tenant {
|
|
||||||
selected?: boolean;
|
|
||||||
}
|
|
||||||
|
|
||||||
export class DirectoryListComponent extends React.Component<DirectoryListProps, DirectoryListComponentState> {
|
|
||||||
constructor(props: DirectoryListProps) {
|
|
||||||
super(props);
|
|
||||||
|
|
||||||
this.state = {
|
|
||||||
filterText: "",
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
public render(): JSX.Element {
|
|
||||||
const { directories: originalItems, selectedDirectoryId } = this.props;
|
|
||||||
const { filterText } = this.state;
|
|
||||||
const filteredItems =
|
|
||||||
originalItems && originalItems.length && filterText
|
|
||||||
? originalItems.filter(
|
|
||||||
(directory) =>
|
|
||||||
directory.displayName &&
|
|
||||||
directory.displayName.toLowerCase().indexOf(filterText && filterText.toLowerCase()) >= 0
|
|
||||||
)
|
|
||||||
: originalItems;
|
|
||||||
const filteredItemsSelected = filteredItems.map((t) => {
|
|
||||||
let tenant: ListTenant = t;
|
|
||||||
tenant.selected = t.tenantId === selectedDirectoryId;
|
|
||||||
return tenant;
|
|
||||||
});
|
|
||||||
|
|
||||||
const textFieldProps: ITextFieldProps = {
|
|
||||||
className: "directoryListFilterTextBox",
|
|
||||||
placeholder: "Filter by directory name",
|
|
||||||
onChange: this._onFilterChanged,
|
|
||||||
ariaLabel: "Directory filter text box",
|
|
||||||
};
|
|
||||||
|
|
||||||
// TODO: add magnify glass to search bar with onRenderSuffix
|
|
||||||
return (
|
|
||||||
<ScrollablePane data-is-scrollable="true">
|
|
||||||
<Sticky stickyPosition={StickyPositionType.Header}>
|
|
||||||
<TextField {...textFieldProps} />
|
|
||||||
</Sticky>
|
|
||||||
<List items={filteredItemsSelected} onRenderCell={this._onRenderCell} />
|
|
||||||
</ScrollablePane>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
private _onFilterChanged = (event: React.FormEvent<HTMLInputElement | HTMLTextAreaElement>, text?: string): void => {
|
|
||||||
this.setState({
|
|
||||||
filterText: text,
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
private _onRenderCell = (directory: ListTenant): JSX.Element => {
|
|
||||||
const buttonProps: IButtonProps = {
|
|
||||||
disabled: directory.selected || false,
|
|
||||||
className: "directoryListButton",
|
|
||||||
onClick: this._onNewDirectoryClick,
|
|
||||||
styles: {
|
|
||||||
root: {
|
|
||||||
backgroundColor: "transparent",
|
|
||||||
height: "auto",
|
|
||||||
borderBottom: "1px solid #ccc",
|
|
||||||
padding: "1px 0",
|
|
||||||
width: "100%",
|
|
||||||
},
|
|
||||||
rootDisabled: {
|
|
||||||
backgroundColor: "#f1f1f8",
|
|
||||||
},
|
|
||||||
rootHovered: {
|
|
||||||
backgroundColor: "rgba(85,179,255,.1)",
|
|
||||||
},
|
|
||||||
flexContainer: {
|
|
||||||
height: "auto",
|
|
||||||
justifyContent: "flex-start",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
|
||||||
<DefaultButton {...buttonProps}>
|
|
||||||
<div className="directoryListItem" data-is-focusable={true}>
|
|
||||||
<div className="directoryListItemName">{directory.displayName}</div>
|
|
||||||
<div className="directoryListItemId">{directory.tenantId}</div>
|
|
||||||
</div>
|
|
||||||
</DefaultButton>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
private _onNewDirectoryClick = (e: React.MouseEvent<HTMLButtonElement>): void => {
|
|
||||||
if (!e || !e.currentTarget) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const buttonElement = e.currentTarget;
|
|
||||||
const selectedDirectoryId = buttonElement.getElementsByClassName("directoryListItemId")[0].textContent;
|
|
||||||
const selectedDirectory = _.find(this.props.directories, (d) => d.tenantId === selectedDirectoryId);
|
|
||||||
|
|
||||||
this.props.onNewDirectorySelected(selectedDirectory);
|
|
||||||
};
|
|
||||||
}
|
|
||||||
@@ -1,93 +0,0 @@
|
|||||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
|
||||||
|
|
||||||
exports[`test render renders with directories and default 1`] = `
|
|
||||||
<Dropdown
|
|
||||||
className="defaultDirectoryDropdown"
|
|
||||||
defaultSelectedKey="asdfghjklzxcvbnm9876543210"
|
|
||||||
label="Set your default directory"
|
|
||||||
onChange={[Function]}
|
|
||||||
options={
|
|
||||||
Array [
|
|
||||||
Object {
|
|
||||||
"key": "lastVisited",
|
|
||||||
"text": "Sign in to your last visited directory",
|
|
||||||
},
|
|
||||||
Object {
|
|
||||||
"key": "asdfghjklzxcvbnm9876543210",
|
|
||||||
"text": "Macrohard(asdfghjklzxcvbnm9876543210)",
|
|
||||||
},
|
|
||||||
Object {
|
|
||||||
"key": "",
|
|
||||||
"text": "()",
|
|
||||||
},
|
|
||||||
]
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
`;
|
|
||||||
|
|
||||||
exports[`test render renders with directories and last visit default 1`] = `
|
|
||||||
<Dropdown
|
|
||||||
className="defaultDirectoryDropdown"
|
|
||||||
defaultSelectedKey="lastVisited"
|
|
||||||
label="Set your default directory"
|
|
||||||
onChange={[Function]}
|
|
||||||
options={
|
|
||||||
Array [
|
|
||||||
Object {
|
|
||||||
"key": "lastVisited",
|
|
||||||
"text": "Sign in to your last visited directory",
|
|
||||||
},
|
|
||||||
Object {
|
|
||||||
"key": "asdfghjklzxcvbnm9876543210",
|
|
||||||
"text": "Macrohard(asdfghjklzxcvbnm9876543210)",
|
|
||||||
},
|
|
||||||
Object {
|
|
||||||
"key": "",
|
|
||||||
"text": "()",
|
|
||||||
},
|
|
||||||
]
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
`;
|
|
||||||
|
|
||||||
exports[`test render renders with directories but no default 1`] = `
|
|
||||||
<Dropdown
|
|
||||||
className="defaultDirectoryDropdown"
|
|
||||||
defaultSelectedKey="lastVisited"
|
|
||||||
label="Set your default directory"
|
|
||||||
onChange={[Function]}
|
|
||||||
options={
|
|
||||||
Array [
|
|
||||||
Object {
|
|
||||||
"key": "lastVisited",
|
|
||||||
"text": "Sign in to your last visited directory",
|
|
||||||
},
|
|
||||||
Object {
|
|
||||||
"key": "asdfghjklzxcvbnm9876543210",
|
|
||||||
"text": "Macrohard(asdfghjklzxcvbnm9876543210)",
|
|
||||||
},
|
|
||||||
Object {
|
|
||||||
"key": "",
|
|
||||||
"text": "()",
|
|
||||||
},
|
|
||||||
]
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
`;
|
|
||||||
|
|
||||||
exports[`test render renders with no directories 1`] = `
|
|
||||||
<Dropdown
|
|
||||||
className="defaultDirectoryDropdown"
|
|
||||||
defaultSelectedKey="lastVisited"
|
|
||||||
label="Set your default directory"
|
|
||||||
onChange={[Function]}
|
|
||||||
options={
|
|
||||||
Array [
|
|
||||||
Object {
|
|
||||||
"key": "lastVisited",
|
|
||||||
"text": "Sign in to your last visited directory",
|
|
||||||
},
|
|
||||||
]
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
`;
|
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -1,16 +0,0 @@
|
|||||||
@import "../../../../less/Common/Constants.less";
|
|
||||||
|
|
||||||
.radioSwitchComponent {
|
|
||||||
cursor: pointer;
|
|
||||||
display: flex;
|
|
||||||
|
|
||||||
&>span:nth-child(n+2) {
|
|
||||||
margin-left: 10px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.caption {
|
|
||||||
color: @BaseDark;
|
|
||||||
padding-left: @SmallSpace;
|
|
||||||
vertical-align: top;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,51 +0,0 @@
|
|||||||
/**
|
|
||||||
* Horizontal switch component
|
|
||||||
*/
|
|
||||||
|
|
||||||
import { Icon } from "@fluentui/react";
|
|
||||||
import * as React from "react";
|
|
||||||
import { NormalizedEventKey } from "../../../Common/Constants";
|
|
||||||
import "./RadioSwitchComponent.less";
|
|
||||||
|
|
||||||
export interface Choice {
|
|
||||||
key: string;
|
|
||||||
onSelect: () => void;
|
|
||||||
label: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface RadioSwitchComponentProps {
|
|
||||||
choices: Choice[];
|
|
||||||
selectedKey: string;
|
|
||||||
onSelectionKeyChange?: (newValue: string) => void;
|
|
||||||
}
|
|
||||||
|
|
||||||
export class RadioSwitchComponent extends React.Component<RadioSwitchComponentProps> {
|
|
||||||
public render(): JSX.Element {
|
|
||||||
return (
|
|
||||||
<div className="radioSwitchComponent">
|
|
||||||
{this.props.choices.map((choice: Choice) => (
|
|
||||||
<span
|
|
||||||
tabIndex={0}
|
|
||||||
key={choice.key}
|
|
||||||
onClick={() => this.onSelect(choice)}
|
|
||||||
onKeyPress={(event) => this.onKeyPress(event, choice)}
|
|
||||||
>
|
|
||||||
<Icon iconName={this.props.selectedKey === choice.key ? "RadioBtnOn" : "RadioBtnOff"} />
|
|
||||||
<span className="caption">{choice.label}</span>
|
|
||||||
</span>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
private onSelect(choice: Choice): void {
|
|
||||||
this.props.onSelectionKeyChange && this.props.onSelectionKeyChange(choice.key);
|
|
||||||
choice.onSelect();
|
|
||||||
}
|
|
||||||
|
|
||||||
private onKeyPress(event: React.KeyboardEvent<HTMLSpanElement>, choice: Choice): void {
|
|
||||||
if (event.key === NormalizedEventKey.Enter || event.key === NormalizedEventKey.Space) {
|
|
||||||
this.onSelect(choice);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,45 +0,0 @@
|
|||||||
/**
|
|
||||||
* Generic abstract React component that senses its dimensions.
|
|
||||||
* It updates its state and re-renders if dimensions change.
|
|
||||||
*/
|
|
||||||
|
|
||||||
import * as React from "react";
|
|
||||||
import * as ResizeSensor from "css-element-queries/src/ResizeSensor";
|
|
||||||
|
|
||||||
export abstract class ResizeSensorComponent<P, S> extends React.Component<P, S> {
|
|
||||||
private isSensing: boolean = false;
|
|
||||||
private resizeSensor: any;
|
|
||||||
|
|
||||||
public constructor(props: P) {
|
|
||||||
super(props);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected abstract onDimensionsChanged(width: number, height: number): void;
|
|
||||||
protected abstract getSensorTarget(): HTMLElement;
|
|
||||||
|
|
||||||
public componentDidUpdate(): void {
|
|
||||||
if (this.isSensing) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const bar = this.getSensorTarget();
|
|
||||||
if (bar.clientWidth > 0 || bar.clientHeight > 0) {
|
|
||||||
const oldPosition = bar.style.position;
|
|
||||||
// TODO Find a better way to use constructor
|
|
||||||
this.resizeSensor = new (ResizeSensor as any)(bar, () => {
|
|
||||||
this.onDimensionsChanged(bar.clientWidth, bar.clientHeight);
|
|
||||||
});
|
|
||||||
this.isSensing = true;
|
|
||||||
|
|
||||||
// ResizeSensor.js sets position to 'relative' which makes the dropdown menu appear clipped.
|
|
||||||
// Undoing doesn't seem to affect resize sensing functionality.
|
|
||||||
bar.style.position = oldPosition;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public componentWillUnmount(): void {
|
|
||||||
if (!!this.resizeSensor) {
|
|
||||||
this.resizeSensor.detach();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -202,10 +202,12 @@ export class ScaleComponent extends React.Component<ScaleComponentProps> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private getFreeTierInfoMessage(): JSX.Element {
|
private getFreeTierInfoMessage(): JSX.Element {
|
||||||
|
const freeTierLimits = SharedConstants.FreeTierLimits;
|
||||||
return (
|
return (
|
||||||
<Text>
|
<Text>
|
||||||
With free tier, you will get the first 400 RU/s and 5 GB of storage in this account for free. To keep your
|
With free tier, you will get the first {freeTierLimits.RU} RU/s and {freeTierLimits.Storage} GB of storage in
|
||||||
account free, keep the total RU/s across all resources in the account to 400 RU/s.
|
this account for free. To keep your account free, keep the total RU/s across all resources in the account to{" "}
|
||||||
|
{freeTierLimits.RU} RU/s.
|
||||||
<Link
|
<Link
|
||||||
href="https://docs.microsoft.com/en-us/azure/cosmos-db/understand-your-bill#billing-examples-with-free-tier-accounts"
|
href="https://docs.microsoft.com/en-us/azure/cosmos-db/understand-your-bill#billing-examples-with-free-tier-accounts"
|
||||||
target="_blank"
|
target="_blank"
|
||||||
|
|||||||
@@ -155,7 +155,9 @@ export class ThroughputInputAutoPilotV3Component extends React.Component<
|
|||||||
this.state = {
|
this.state = {
|
||||||
spendAckChecked: this.props.spendAckChecked,
|
spendAckChecked: this.props.spendAckChecked,
|
||||||
exceedFreeTierThroughput:
|
exceedFreeTierThroughput:
|
||||||
this.props.isFreeTierAccount && !this.props.isAutoPilotSelected && this.props.throughput > 400,
|
this.props.isFreeTierAccount &&
|
||||||
|
!this.props.isAutoPilotSelected &&
|
||||||
|
this.props.throughput > SharedConstants.FreeTierLimits.RU,
|
||||||
};
|
};
|
||||||
|
|
||||||
this.step = this.props.step ?? ThroughputInputAutoPilotV3Component.defaultStep;
|
this.step = this.props.step ?? ThroughputInputAutoPilotV3Component.defaultStep;
|
||||||
@@ -441,7 +443,9 @@ export class ThroughputInputAutoPilotV3Component extends React.Component<
|
|||||||
if (this.overrideWithAutoPilotSettings()) {
|
if (this.overrideWithAutoPilotSettings()) {
|
||||||
this.props.onMaxAutoPilotThroughputChange(newThroughput);
|
this.props.onMaxAutoPilotThroughputChange(newThroughput);
|
||||||
} else {
|
} else {
|
||||||
this.setState({ exceedFreeTierThroughput: this.props.isFreeTierAccount && newThroughput > 400 });
|
this.setState({
|
||||||
|
exceedFreeTierThroughput: this.props.isFreeTierAccount && newThroughput > SharedConstants.FreeTierLimits.RU,
|
||||||
|
});
|
||||||
this.props.onThroughputChange(newThroughput);
|
this.props.onThroughputChange(newThroughput);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -581,9 +585,7 @@ export class ThroughputInputAutoPilotV3Component extends React.Component<
|
|||||||
messageBarIconProps={{ iconName: "WarningSolid", className: "messageBarWarningIcon" }}
|
messageBarIconProps={{ iconName: "WarningSolid", className: "messageBarWarningIcon" }}
|
||||||
styles={messageBarStyles}
|
styles={messageBarStyles}
|
||||||
>
|
>
|
||||||
{
|
{`Billing will apply if you provision more than ${SharedConstants.FreeTierLimits.RU} RU/s of manual throughput, or if the resource scales beyond ${SharedConstants.FreeTierLimits.RU} RU/s with autoscale.`}
|
||||||
"Billing will apply if you provision more than 400 RU/s of manual throughput, or if the resource scales beyond 400 RU/s with autoscale."
|
|
||||||
}
|
|
||||||
</MessageBar>
|
</MessageBar>
|
||||||
)}
|
)}
|
||||||
{this.props.getThroughputWarningMessage() && (
|
{this.props.getThroughputWarningMessage() && (
|
||||||
|
|||||||
@@ -187,9 +187,8 @@ export const ThroughputInput: FunctionComponent<ThroughputInputProps> = ({
|
|||||||
<TooltipHost
|
<TooltipHost
|
||||||
directionalHint={DirectionalHint.topLeftEdge}
|
directionalHint={DirectionalHint.topLeftEdge}
|
||||||
content={
|
content={
|
||||||
showFreeTierExceedThroughputTooltip &&
|
showFreeTierExceedThroughputTooltip && throughput > SharedConstants.FreeTierLimits.RU
|
||||||
throughput > SharedConstants.CollectionCreation.DefaultCollectionRUs400
|
? `The first ${SharedConstants.FreeTierLimits.RU} RU/s in this account are free. Billing will apply to any throughput beyond ${SharedConstants.FreeTierLimits.RU} RU/s.`
|
||||||
? "The first 400 RU/s in this account are free. Billing will apply to any throughput beyond 400 RU/s."
|
|
||||||
: undefined
|
: undefined
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
|
|||||||
@@ -1,13 +1,13 @@
|
|||||||
import * as AutoPilotUtils from "../../../Utils/AutoPilotUtils";
|
|
||||||
import * as ko from "knockout";
|
import * as ko from "knockout";
|
||||||
import * as ViewModels from "../../../Contracts/ViewModels";
|
|
||||||
import ThroughputInputComponentAutoscaleV3 from "./ThroughputInputComponentAutoscaleV3.html";
|
|
||||||
import { KeyCodes } from "../../../Common/Constants";
|
import { KeyCodes } from "../../../Common/Constants";
|
||||||
import { WaitsForTemplateViewModel } from "../../WaitsForTemplateViewModel";
|
import * as ViewModels from "../../../Contracts/ViewModels";
|
||||||
|
import { FreeTierLimits } from "../../../Shared/Constants";
|
||||||
import { userContext } from "../../../UserContext";
|
|
||||||
import * as TelemetryProcessor from "../../../Shared/Telemetry/TelemetryProcessor";
|
|
||||||
import { Action, ActionModifiers } from "../../../Shared/Telemetry/TelemetryConstants";
|
import { Action, ActionModifiers } from "../../../Shared/Telemetry/TelemetryConstants";
|
||||||
|
import * as TelemetryProcessor from "../../../Shared/Telemetry/TelemetryProcessor";
|
||||||
|
import * as AutoPilotUtils from "../../../Utils/AutoPilotUtils";
|
||||||
|
import { WaitsForTemplateViewModel } from "../../WaitsForTemplateViewModel";
|
||||||
|
import ThroughputInputComponentAutoscaleV3 from "./ThroughputInputComponentAutoscaleV3.html";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Throughput Input:
|
* Throughput Input:
|
||||||
*
|
*
|
||||||
@@ -236,11 +236,11 @@ export class ThroughputInputViewModel extends WaitsForTemplateViewModel {
|
|||||||
this.freeTierExceedThroughputTooltip = options.freeTierExceedThroughputTooltip || ko.observable<string>();
|
this.freeTierExceedThroughputTooltip = options.freeTierExceedThroughputTooltip || ko.observable<string>();
|
||||||
this.freeTierExceedThroughputWarning = options.freeTierExceedThroughputWarning || ko.observable<string>();
|
this.freeTierExceedThroughputWarning = options.freeTierExceedThroughputWarning || ko.observable<string>();
|
||||||
this.showFreeTierExceedThroughputTooltip = ko.pureComputed<boolean>(
|
this.showFreeTierExceedThroughputTooltip = ko.pureComputed<boolean>(
|
||||||
() => !!this.freeTierExceedThroughputTooltip() && this.value() > 400
|
() => !!this.freeTierExceedThroughputTooltip() && this.value() > FreeTierLimits.RU
|
||||||
);
|
);
|
||||||
|
|
||||||
this.showFreeTierExceedThroughputWarning = ko.pureComputed<boolean>(
|
this.showFreeTierExceedThroughputWarning = ko.pureComputed<boolean>(
|
||||||
() => !!this.freeTierExceedThroughputWarning() && this.value() > 400
|
() => !!this.freeTierExceedThroughputWarning() && this.value() > FreeTierLimits.RU
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ export interface GremlinSimpleClientParameters {
|
|||||||
password: string;
|
password: string;
|
||||||
successCallback: (result: Result) => void;
|
successCallback: (result: Result) => void;
|
||||||
progressCallback: (result: Result) => void;
|
progressCallback: (result: Result) => void;
|
||||||
failureCallback: (result: Result, error: string) => void;
|
failureCallback: (result: Result | null, error: string) => void;
|
||||||
infoCallback: (msg: string) => void;
|
infoCallback: (msg: string) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -62,7 +62,6 @@ export class GremlinSimpleClient {
|
|||||||
private static readonly requestChargeHeader = "x-ms-request-charge";
|
private static readonly requestChargeHeader = "x-ms-request-charge";
|
||||||
|
|
||||||
public params: GremlinSimpleClientParameters;
|
public params: GremlinSimpleClientParameters;
|
||||||
private protocols: string | string[];
|
|
||||||
private ws: WebSocket;
|
private ws: WebSocket;
|
||||||
|
|
||||||
public requestsToSend: { [requestId: string]: GremlinRequestMessage };
|
public requestsToSend: { [requestId: string]: GremlinRequestMessage };
|
||||||
@@ -72,6 +71,7 @@ export class GremlinSimpleClient {
|
|||||||
this.params = params;
|
this.params = params;
|
||||||
this.pendingRequests = {};
|
this.pendingRequests = {};
|
||||||
this.requestsToSend = {};
|
this.requestsToSend = {};
|
||||||
|
this.ws = GremlinSimpleClient.createWebSocket(this.params.endpoint);
|
||||||
}
|
}
|
||||||
|
|
||||||
public connect() {
|
public connect() {
|
||||||
@@ -117,7 +117,7 @@ export class GremlinSimpleClient {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public decodeMessage(msg: MessageEvent): GremlinResponseMessage {
|
public decodeMessage(msg: MessageEvent): GremlinResponseMessage | null {
|
||||||
if (msg.data === null) {
|
if (msg.data === null) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
@@ -280,7 +280,7 @@ export class GremlinSimpleClient {
|
|||||||
|
|
||||||
public static utf8ToB64(utf8Str: string) {
|
public static utf8ToB64(utf8Str: string) {
|
||||||
return btoa(
|
return btoa(
|
||||||
encodeURIComponent(utf8Str).replace(/%([0-9A-F]{2})/g, function (match, p1) {
|
encodeURIComponent(utf8Str).replace(/%([0-9A-F]{2})/g, function (_match, p1) {
|
||||||
return String.fromCharCode(parseInt(p1, 16));
|
return String.fromCharCode(parseInt(p1, 16));
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
@@ -305,7 +305,7 @@ export class GremlinSimpleClient {
|
|||||||
return binaryMessage;
|
return binaryMessage;
|
||||||
}
|
}
|
||||||
|
|
||||||
private onOpen(event: any) {
|
private onOpen(_event: any) {
|
||||||
this.executeRequestsToSend();
|
this.executeRequestsToSend();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,12 +1,11 @@
|
|||||||
/* eslint jsx-a11y/no-static-element-interactions: 0 */
|
/* eslint jsx-a11y/no-static-element-interactions: 0 */
|
||||||
/* eslint jsx-a11y/click-events-have-key-events: 0 */
|
/* eslint jsx-a11y/click-events-have-key-events: 0 */
|
||||||
|
|
||||||
|
import { actions, AppState, ContentRef, selectors } from "@nteract/core";
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import { connect } from "react-redux";
|
import { connect } from "react-redux";
|
||||||
import { Dispatch } from "redux";
|
import { Dispatch } from "redux";
|
||||||
|
|
||||||
import { actions, selectors, ContentRef, AppState } from "@nteract/core";
|
|
||||||
|
|
||||||
interface ComponentProps {
|
interface ComponentProps {
|
||||||
id: string;
|
id: string;
|
||||||
contentRef: ContentRef;
|
contentRef: ContentRef;
|
||||||
@@ -70,7 +69,7 @@ export class HijackScroll extends React.Component<Props> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const makeMapStateToProps = (initialState: AppState, ownProps: ComponentProps) => {
|
const makeMapStateToProps = (_initialState: AppState, ownProps: ComponentProps) => {
|
||||||
const mapStateToProps = (state: AppState) => {
|
const mapStateToProps = (state: AppState) => {
|
||||||
const { id, contentRef } = ownProps;
|
const { id, contentRef } = ownProps;
|
||||||
const model = selectors.model(state, { contentRef });
|
const model = selectors.model(state, { contentRef });
|
||||||
@@ -87,7 +86,7 @@ const makeMapStateToProps = (initialState: AppState, ownProps: ComponentProps) =
|
|||||||
return mapStateToProps;
|
return mapStateToProps;
|
||||||
};
|
};
|
||||||
|
|
||||||
const makeMapDispatchToProps = (initialDispatch: Dispatch, ownProps: ComponentProps) => {
|
const makeMapDispatchToProps = (_initialDispatch: Dispatch, ownProps: ComponentProps) => {
|
||||||
const mapDispatchToProps = (dispatch: Dispatch) => ({
|
const mapDispatchToProps = (dispatch: Dispatch) => ({
|
||||||
selectCell: () => dispatch(actions.focusCell({ id: ownProps.id, contentRef: ownProps.contentRef })),
|
selectCell: () => dispatch(actions.focusCell({ id: ownProps.id, contentRef: ownProps.contentRef })),
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,11 +1,10 @@
|
|||||||
|
import { CellId } from "@nteract/commutable";
|
||||||
|
import { actions, AppState, ContentRef, selectors } from "@nteract/core";
|
||||||
import Immutable from "immutable";
|
import Immutable from "immutable";
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import { connect } from "react-redux";
|
import { connect } from "react-redux";
|
||||||
import { Dispatch } from "redux";
|
import { Dispatch } from "redux";
|
||||||
|
|
||||||
import { CellId } from "@nteract/commutable";
|
|
||||||
import { actions, AppState, ContentRef, selectors } from "@nteract/core";
|
|
||||||
|
|
||||||
interface ComponentProps {
|
interface ComponentProps {
|
||||||
contentRef: ContentRef;
|
contentRef: ContentRef;
|
||||||
children: React.ReactNode;
|
children: React.ReactNode;
|
||||||
@@ -107,7 +106,7 @@ export class KeyboardShortcuts extends React.Component<Props> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export const makeMapStateToProps = (state: AppState, ownProps: ComponentProps) => {
|
export const makeMapStateToProps = (_state: AppState, ownProps: ComponentProps) => {
|
||||||
const { contentRef } = ownProps;
|
const { contentRef } = ownProps;
|
||||||
const mapStateToProps = (state: AppState) => {
|
const mapStateToProps = (state: AppState) => {
|
||||||
const model = selectors.model(state, { contentRef });
|
const model = selectors.model(state, { contentRef });
|
||||||
|
|||||||
@@ -36,7 +36,7 @@ export const PanelInfoErrorComponent: React.FunctionComponent<PanelInfoErrorProp
|
|||||||
{message}
|
{message}
|
||||||
{link && linkText && (
|
{link && linkText && (
|
||||||
<Link target="_blank" href={link}>
|
<Link target="_blank" href={link}>
|
||||||
{linkText}
|
{" " + linkText}
|
||||||
</Link>
|
</Link>
|
||||||
)}
|
)}
|
||||||
</Text>
|
</Text>
|
||||||
|
|||||||
@@ -17,6 +17,8 @@ import * as _ from "underscore";
|
|||||||
import { sendMessage } from "../Common/MessageHandler";
|
import { sendMessage } from "../Common/MessageHandler";
|
||||||
import { SelfServeMessageTypes } from "../Contracts/SelfServeContracts";
|
import { SelfServeMessageTypes } from "../Contracts/SelfServeContracts";
|
||||||
import { SmartUiComponent, SmartUiDescriptor } from "../Explorer/Controls/SmartUi/SmartUiComponent";
|
import { SmartUiComponent, SmartUiDescriptor } from "../Explorer/Controls/SmartUi/SmartUiComponent";
|
||||||
|
import { Action, ActionModifiers } from "../Shared/Telemetry/TelemetryConstants";
|
||||||
|
import { trace } from "../Shared/Telemetry/TelemetryProcessor";
|
||||||
import { commandBarItemStyles, commandBarStyles, containerStackTokens, separatorStyles } from "./SelfServeStyles";
|
import { commandBarItemStyles, commandBarStyles, containerStackTokens, separatorStyles } from "./SelfServeStyles";
|
||||||
import {
|
import {
|
||||||
AnyDisplay,
|
AnyDisplay,
|
||||||
@@ -27,6 +29,7 @@ import {
|
|||||||
Node,
|
Node,
|
||||||
NumberInput,
|
NumberInput,
|
||||||
RefreshResult,
|
RefreshResult,
|
||||||
|
SelfServeComponentTelemetryType,
|
||||||
SelfServeDescriptor,
|
SelfServeDescriptor,
|
||||||
SmartUiInput,
|
SmartUiInput,
|
||||||
StringInput,
|
StringInput,
|
||||||
@@ -87,6 +90,12 @@ export class SelfServeComponent extends React.Component<SelfServeComponentProps,
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
this.initializeSmartUiComponent();
|
this.initializeSmartUiComponent();
|
||||||
|
|
||||||
|
const telemetryData = {
|
||||||
|
selfServeClassName: this.props.descriptor.root.id,
|
||||||
|
eventType: SelfServeComponentTelemetryType.Load,
|
||||||
|
};
|
||||||
|
trace(Action.SelfServeComponent, ActionModifiers.Mark, telemetryData, SelfServeMessageTypes.TelemetryInfo);
|
||||||
}
|
}
|
||||||
|
|
||||||
constructor(props: SelfServeComponentProps) {
|
constructor(props: SelfServeComponentProps) {
|
||||||
@@ -262,6 +271,12 @@ export class SelfServeComponent extends React.Component<SelfServeComponentProps,
|
|||||||
};
|
};
|
||||||
|
|
||||||
public performSave = async (): Promise<void> => {
|
public performSave = async (): Promise<void> => {
|
||||||
|
const telemetryData = {
|
||||||
|
selfServeClassName: this.props.descriptor.root.id,
|
||||||
|
eventType: SelfServeComponentTelemetryType.Save,
|
||||||
|
};
|
||||||
|
trace(Action.SelfServeComponent, ActionModifiers.Mark, telemetryData, SelfServeMessageTypes.TelemetryInfo);
|
||||||
|
|
||||||
this.setState({ isSaving: true, notification: undefined });
|
this.setState({ isSaving: true, notification: undefined });
|
||||||
try {
|
try {
|
||||||
const onSaveResult = await this.props.descriptor.onSave(
|
const onSaveResult = await this.props.descriptor.onSave(
|
||||||
|
|||||||
@@ -70,6 +70,12 @@ export interface SelfServeDescriptor {
|
|||||||
refreshParams?: RefreshParams;
|
refreshParams?: RefreshParams;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**@internal */
|
||||||
|
export enum SelfServeComponentTelemetryType {
|
||||||
|
Load = "Load",
|
||||||
|
Save = "Save",
|
||||||
|
}
|
||||||
|
|
||||||
/**@internal */
|
/**@internal */
|
||||||
export type AnyDisplay = NumberInput | BooleanInput | StringInput | ChoiceInput | DescriptionDisplay;
|
export type AnyDisplay = NumberInput | BooleanInput | StringInput | ChoiceInput | DescriptionDisplay;
|
||||||
|
|
||||||
|
|||||||
@@ -201,3 +201,8 @@ export class SubscriptionUtilMappings {
|
|||||||
export class AutopilotDocumentation {
|
export class AutopilotDocumentation {
|
||||||
public static Url: string = "https://aka.ms/cosmos-autoscale-info";
|
public static Url: string = "https://aka.ms/cosmos-autoscale-info";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export class FreeTierLimits {
|
||||||
|
public static RU: number = 1000;
|
||||||
|
public static Storage: number = 25;
|
||||||
|
}
|
||||||
|
|||||||
@@ -117,6 +117,7 @@ export enum Action {
|
|||||||
SelfServe,
|
SelfServe,
|
||||||
ExpandAddCollectionPaneAdvancedSection,
|
ExpandAddCollectionPaneAdvancedSection,
|
||||||
SchemaAnalyzerClickAnalyze,
|
SchemaAnalyzerClickAnalyze,
|
||||||
|
SelfServeComponent,
|
||||||
}
|
}
|
||||||
|
|
||||||
export const ActionModifiers = {
|
export const ActionModifiers = {
|
||||||
|
|||||||
@@ -267,9 +267,11 @@ export function getUpsellMessage(
|
|||||||
if (isFreeTier) {
|
if (isFreeTier) {
|
||||||
const collectionName = getCollectionName().toLocaleLowerCase();
|
const collectionName = getCollectionName().toLocaleLowerCase();
|
||||||
const resourceType = isCollection ? collectionName : "database";
|
const resourceType = isCollection ? collectionName : "database";
|
||||||
|
const freeTierMaxRU = Constants.FreeTierLimits.RU;
|
||||||
|
const freeTierMaxStorage = Constants.FreeTierLimits.Storage;
|
||||||
return isFirstResourceCreated
|
return isFirstResourceCreated
|
||||||
? `The free tier discount of 400 RU/s has already been applied to a database or ${collectionName} in this account. Billing will apply to this ${resourceType} after it is created.`
|
? `Your account currently has at least 1 database or ${collectionName} with provisioned RU/s. Billing will apply to this ${resourceType} if the total RU/s in your account exceeds ${freeTierMaxRU} RU/s.`
|
||||||
: `With free tier, you'll get the first 400 RU/s and 5 GB of storage in this account for free. Billing will apply if you provision more than 400 RU/s of manual throughput, or if the ${resourceType} scales beyond 400 RU/s with autoscale.`;
|
: `With free tier, you'll get the first ${freeTierMaxRU} RU/s and ${freeTierMaxStorage} GB of storage in this account for free. Billing will apply if you provision more than ${freeTierMaxRU} RU/s of manual throughput, or if the ${resourceType} scales beyond ${freeTierMaxRU} RU/s with autoscale.`;
|
||||||
} else {
|
} else {
|
||||||
let price: number = Constants.OfferPricing.MonthlyPricing.default.Standard.StartingPrice;
|
let price: number = Constants.OfferPricing.MonthlyPricing.default.Standard.StartingPrice;
|
||||||
|
|
||||||
|
|||||||
@@ -4,8 +4,8 @@ import { ConfigContext, initializeConfiguration } from "../ConfigContext";
|
|||||||
// This hook initializes global configuration from a config.json file that is injected at deploy time
|
// This hook initializes global configuration from a config.json file that is injected at deploy time
|
||||||
// This allows the same main Data Explorer build to be exactly the same in all clouds/platforms,
|
// This allows the same main Data Explorer build to be exactly the same in all clouds/platforms,
|
||||||
// but override some of the configuration as nesssary
|
// but override some of the configuration as nesssary
|
||||||
export function useConfig(): Readonly<ConfigContext> {
|
export function useConfig(): Readonly<ConfigContext | undefined> {
|
||||||
const [state, setState] = useState<ConfigContext>();
|
const [state, setState] = useState<ConfigContext | undefined>();
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
initializeConfiguration().then((response) => setState(response));
|
initializeConfiguration().then((response) => setState(response));
|
||||||
|
|||||||
@@ -8,6 +8,7 @@
|
|||||||
"noUnusedParameters": true
|
"noUnusedParameters": true
|
||||||
},
|
},
|
||||||
"files": [
|
"files": [
|
||||||
|
"src/Explorer/Graph/GraphExplorerComponent/GremlinSimpleClient.ts",
|
||||||
"./src/AuthType.ts",
|
"./src/AuthType.ts",
|
||||||
"./src/Bindings/ReactBindingHandler.ts",
|
"./src/Bindings/ReactBindingHandler.ts",
|
||||||
"./src/Common/ArrayHashMap.ts",
|
"./src/Common/ArrayHashMap.ts",
|
||||||
@@ -114,6 +115,7 @@
|
|||||||
"./src/Utils/StyleUtils.ts",
|
"./src/Utils/StyleUtils.ts",
|
||||||
"./src/Utils/WindowUtils.test.ts",
|
"./src/Utils/WindowUtils.test.ts",
|
||||||
"./src/Utils/WindowUtils.ts",
|
"./src/Utils/WindowUtils.ts",
|
||||||
|
"./src/hooks/useConfig.ts",
|
||||||
"./src/hooks/useDirectories.tsx",
|
"./src/hooks/useDirectories.tsx",
|
||||||
"./src/hooks/useFullScreenURLs.tsx",
|
"./src/hooks/useFullScreenURLs.tsx",
|
||||||
"./src/hooks/useGraphPhoto.tsx",
|
"./src/hooks/useGraphPhoto.tsx",
|
||||||
@@ -121,7 +123,8 @@
|
|||||||
"./src/i18n.ts",
|
"./src/i18n.ts",
|
||||||
"./src/quickstart.ts",
|
"./src/quickstart.ts",
|
||||||
"./src/setupTests.ts",
|
"./src/setupTests.ts",
|
||||||
"./src/userContext.test.ts"
|
"./src/userContext.test.ts",
|
||||||
|
"src/Common/EntityValue.tsx"
|
||||||
],
|
],
|
||||||
"include": [
|
"include": [
|
||||||
"src/CellOutputViewer/transforms/**/*",
|
"src/CellOutputViewer/transforms/**/*",
|
||||||
@@ -135,6 +138,8 @@
|
|||||||
"src/Explorer/Controls/ResizeSensorReactComponent/**/*",
|
"src/Explorer/Controls/ResizeSensorReactComponent/**/*",
|
||||||
"src/Explorer/Graph/GraphExplorerComponent/__mocks__/**/*",
|
"src/Explorer/Graph/GraphExplorerComponent/__mocks__/**/*",
|
||||||
"src/Explorer/Notebook/NotebookComponent/__mocks__/**/*",
|
"src/Explorer/Notebook/NotebookComponent/__mocks__/**/*",
|
||||||
|
"src/Explorer/Notebook/NotebookRenderer/decorators/hijack-scroll/**/*",
|
||||||
|
"src/Explorer/Notebook/NotebookRenderer/decorators/kbd-shortcuts/**/*",
|
||||||
"src/Explorer/Panes/RightPaneForm/**/*",
|
"src/Explorer/Panes/RightPaneForm/**/*",
|
||||||
"src/Libs/**/*",
|
"src/Libs/**/*",
|
||||||
"src/Localization/**/*",
|
"src/Localization/**/*",
|
||||||
@@ -144,4 +149,4 @@
|
|||||||
"src/Terminal/**/*",
|
"src/Terminal/**/*",
|
||||||
"src/Utils/arm/**/*"
|
"src/Utils/arm/**/*"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user