mirror of
https://github.com/Azure/cosmos-explorer.git
synced 2025-12-22 02:11:29 +00:00
Rename index.tsx to {class name}.tsx (#689)
* Rename index.tsx to {class name}.tsx
* Update tests
Co-authored-by: Steve Faulkner <southpolesteve@gmail.com>
This commit is contained in:
@@ -0,0 +1,37 @@
|
||||
import { mount } from "enzyme";
|
||||
import React from "react";
|
||||
import Explorer from "../../Explorer";
|
||||
import StoredProcedure from "../../Tree/StoredProcedure";
|
||||
import { ExecuteSprocParamsPane } from "./ExecuteSprocParamsPane";
|
||||
|
||||
describe("Excute Sproc Param Pane", () => {
|
||||
const fakeExplorer = {} as Explorer;
|
||||
const fakeSproc = {} as StoredProcedure;
|
||||
const props = {
|
||||
explorer: fakeExplorer,
|
||||
storedProcedure: fakeSproc,
|
||||
closePanel: (): void => undefined,
|
||||
};
|
||||
|
||||
it("should render Default properly", () => {
|
||||
const wrapper = mount(<ExecuteSprocParamsPane {...props} />);
|
||||
expect(wrapper).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it("initially display 2 input field, 1 partition and 1 parameter", () => {
|
||||
const wrapper = mount(<ExecuteSprocParamsPane {...props} />);
|
||||
expect(wrapper.find("input[type='text']")).toHaveLength(2);
|
||||
});
|
||||
|
||||
it("add a new parameter field", () => {
|
||||
const wrapper = mount(<ExecuteSprocParamsPane {...props} />);
|
||||
wrapper.find("#addparam").last().simulate("click");
|
||||
expect(wrapper.find("input[type='text']")).toHaveLength(3);
|
||||
});
|
||||
|
||||
it("remove a parameter field", () => {
|
||||
const wrapper = mount(<ExecuteSprocParamsPane {...props} />);
|
||||
wrapper.find("#deleteparam").last().simulate("click");
|
||||
expect(wrapper.find("input[type='text']")).toHaveLength(1);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,175 @@
|
||||
import { useBoolean } from "@uifabric/react-hooks";
|
||||
import { IDropdownOption, IImageProps, Image, Stack, Text } from "office-ui-fabric-react";
|
||||
import React, { FunctionComponent, useState } from "react";
|
||||
import AddPropertyIcon from "../../../../images/Add-property.svg";
|
||||
import Explorer from "../../Explorer";
|
||||
import StoredProcedure from "../../Tree/StoredProcedure";
|
||||
import {
|
||||
GenericRightPaneComponent,
|
||||
GenericRightPaneProps,
|
||||
} from "../GenericRightPaneComponent/GenericRightPaneComponent";
|
||||
import { InputParameter } from "./InputParameter";
|
||||
|
||||
interface ExecuteSprocParamsPaneProps {
|
||||
explorer: Explorer;
|
||||
storedProcedure: StoredProcedure;
|
||||
closePanel: () => void;
|
||||
}
|
||||
|
||||
const imageProps: IImageProps = {
|
||||
width: 20,
|
||||
height: 30,
|
||||
};
|
||||
|
||||
interface UnwrappedExecuteSprocParam {
|
||||
key: string;
|
||||
text: string;
|
||||
}
|
||||
|
||||
export const ExecuteSprocParamsPane: FunctionComponent<ExecuteSprocParamsPaneProps> = ({
|
||||
explorer,
|
||||
storedProcedure,
|
||||
closePanel,
|
||||
}: ExecuteSprocParamsPaneProps): JSX.Element => {
|
||||
const [isLoading, { setTrue: setLoadingTrue, setFalse: setLoadingFalse }] = useBoolean(false);
|
||||
const [paramKeyValues, setParamKeyValues] = useState<UnwrappedExecuteSprocParam[]>([{ key: "string", text: "" }]);
|
||||
const [partitionValue, setPartitionValue] = useState<string>(); // Defaulting to undefined here is important. It is not the same partition key as ""
|
||||
const [selectedKey, setSelectedKey] = React.useState<IDropdownOption>({ key: "string", text: "" });
|
||||
const [formError, setFormError] = useState<string>("");
|
||||
const [formErrorsDetails, setFormErrorsDetails] = useState<string>("");
|
||||
|
||||
const onPartitionKeyChange = (event: React.FormEvent<HTMLDivElement>, item: IDropdownOption): void => {
|
||||
setSelectedKey(item);
|
||||
};
|
||||
|
||||
const genericPaneProps: GenericRightPaneProps = {
|
||||
container: explorer,
|
||||
formError: formError,
|
||||
formErrorDetail: formErrorsDetails,
|
||||
id: "executesprocparamspane",
|
||||
isExecuting: isLoading,
|
||||
title: "Input parameters",
|
||||
submitButtonText: "Execute",
|
||||
onClose: () => closePanel(),
|
||||
onSubmit: () => submit(),
|
||||
};
|
||||
|
||||
const validateUnwrappedParams = (): boolean => {
|
||||
const unwrappedParams: UnwrappedExecuteSprocParam[] = paramKeyValues;
|
||||
for (let i = 0; i < unwrappedParams.length; i++) {
|
||||
const { key: paramType, text: paramValue } = unwrappedParams[i];
|
||||
if (paramType === "custom" && (paramValue === "" || paramValue === undefined)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
};
|
||||
|
||||
const setInvalidParamError = (invalidParam: string): void => {
|
||||
setFormError(`Invalid param specified: ${invalidParam}`);
|
||||
setFormErrorsDetails(`Invalid param specified: ${invalidParam} is not a valid literal value`);
|
||||
};
|
||||
|
||||
const submit = (): void => {
|
||||
const wrappedSprocParams: UnwrappedExecuteSprocParam[] = paramKeyValues;
|
||||
const { key: partitionKey } = selectedKey;
|
||||
if (partitionKey === "custom" && (partitionValue === "" || partitionValue === undefined)) {
|
||||
setInvalidParamError(partitionValue);
|
||||
return;
|
||||
}
|
||||
if (!validateUnwrappedParams()) {
|
||||
setInvalidParamError("");
|
||||
return;
|
||||
}
|
||||
setLoadingTrue();
|
||||
const sprocParams =
|
||||
wrappedSprocParams &&
|
||||
wrappedSprocParams.map((sprocParam) => {
|
||||
if (sprocParam.key === "custom") {
|
||||
return JSON.parse(sprocParam.text);
|
||||
}
|
||||
return sprocParam.text;
|
||||
});
|
||||
storedProcedure.execute(sprocParams, partitionKey === "custom" ? JSON.parse(partitionValue) : partitionValue);
|
||||
setLoadingFalse();
|
||||
closePanel();
|
||||
};
|
||||
|
||||
const deleteParamAtIndex = (indexToRemove: number): void => {
|
||||
const cloneParamKeyValue = [...paramKeyValues];
|
||||
cloneParamKeyValue.splice(indexToRemove, 1);
|
||||
setParamKeyValues(cloneParamKeyValue);
|
||||
};
|
||||
|
||||
const addNewParamAtIndex = (indexToAdd: number): void => {
|
||||
const cloneParamKeyValue = [...paramKeyValues];
|
||||
cloneParamKeyValue.splice(indexToAdd, 0, { key: "string", text: "" });
|
||||
setParamKeyValues(cloneParamKeyValue);
|
||||
};
|
||||
|
||||
const paramValueChange = (value: string, indexOfInput: number): void => {
|
||||
const cloneParamKeyValue = [...paramKeyValues];
|
||||
cloneParamKeyValue[indexOfInput].text = value;
|
||||
setParamKeyValues(cloneParamKeyValue);
|
||||
};
|
||||
|
||||
const paramKeyChange = (
|
||||
_event: React.FormEvent<HTMLDivElement>,
|
||||
selectedParam: IDropdownOption,
|
||||
indexOfParam: number
|
||||
): void => {
|
||||
const cloneParamKeyValue = [...paramKeyValues];
|
||||
cloneParamKeyValue[indexOfParam].key = selectedParam.key.toString();
|
||||
setParamKeyValues(cloneParamKeyValue);
|
||||
};
|
||||
|
||||
const addNewParamAtLastIndex = (): void => {
|
||||
const cloneParamKeyValue = [...paramKeyValues];
|
||||
cloneParamKeyValue.splice(cloneParamKeyValue.length, 0, { key: "string", text: "" });
|
||||
setParamKeyValues(cloneParamKeyValue);
|
||||
};
|
||||
|
||||
return (
|
||||
<GenericRightPaneComponent {...genericPaneProps}>
|
||||
<div className="panelFormWrapper">
|
||||
<div className="panelMainContent">
|
||||
<InputParameter
|
||||
dropdownLabel="Key"
|
||||
inputParameterTitle="Partition key value"
|
||||
inputLabel="Value"
|
||||
isAddRemoveVisible={false}
|
||||
onParamValueChange={(_event, newInput?: string) => {
|
||||
setPartitionValue(newInput);
|
||||
}}
|
||||
onParamKeyChange={onPartitionKeyChange}
|
||||
paramValue={partitionValue}
|
||||
selectedKey={selectedKey.key}
|
||||
/>
|
||||
{paramKeyValues.map((paramKeyValue, index) => (
|
||||
<InputParameter
|
||||
key={paramKeyValue && paramKeyValue.text + index}
|
||||
dropdownLabel={!index && "Key"}
|
||||
inputParameterTitle={!index && "Enter input parameters (if any)"}
|
||||
inputLabel={!index && "Param"}
|
||||
isAddRemoveVisible={true}
|
||||
onDeleteParamKeyPress={() => deleteParamAtIndex(index)}
|
||||
onAddNewParamKeyPress={() => addNewParamAtIndex(index + 1)}
|
||||
onParamValueChange={(event, newInput?: string) => {
|
||||
paramValueChange(newInput, index);
|
||||
}}
|
||||
onParamKeyChange={(event: React.FormEvent<HTMLDivElement>, selectedParam: IDropdownOption) => {
|
||||
paramKeyChange(event, selectedParam, index);
|
||||
}}
|
||||
paramValue={paramKeyValue && paramKeyValue.text}
|
||||
selectedKey={paramKeyValue && paramKeyValue.key}
|
||||
/>
|
||||
))}
|
||||
<Stack horizontal onClick={addNewParamAtLastIndex}>
|
||||
<Image {...imageProps} src={AddPropertyIcon} alt="Add param" />
|
||||
<Text className="addNewParamStyle">Add New Param</Text>
|
||||
</Stack>
|
||||
</div>
|
||||
</div>
|
||||
</GenericRightPaneComponent>
|
||||
);
|
||||
};
|
||||
91
src/Explorer/Panes/ExecuteSprocParamsPane/InputParameter.tsx
Normal file
91
src/Explorer/Panes/ExecuteSprocParamsPane/InputParameter.tsx
Normal file
@@ -0,0 +1,91 @@
|
||||
import {
|
||||
Dropdown,
|
||||
IDropdownOption,
|
||||
IDropdownStyles,
|
||||
IImageProps,
|
||||
Image,
|
||||
Label,
|
||||
Stack,
|
||||
TextField,
|
||||
} from "office-ui-fabric-react";
|
||||
import React, { FunctionComponent } from "react";
|
||||
import AddPropertyIcon from "../../../../images/Add-property.svg";
|
||||
import EntityCancelIcon from "../../../../images/Entity_cancel.svg";
|
||||
|
||||
const dropdownStyles: Partial<IDropdownStyles> = { dropdown: { width: 100 } };
|
||||
const options = [
|
||||
{ key: "string", text: "String" },
|
||||
{ key: "custom", text: "Custom" },
|
||||
];
|
||||
|
||||
export interface InputParameterProps {
|
||||
dropdownLabel?: string;
|
||||
inputParameterTitle?: string;
|
||||
inputLabel?: string;
|
||||
isAddRemoveVisible: boolean;
|
||||
onDeleteParamKeyPress?: () => void;
|
||||
onAddNewParamKeyPress?: () => void;
|
||||
onParamValueChange: (event: React.FormEvent<HTMLElement>, newInput?: string) => void;
|
||||
onParamKeyChange: (event: React.FormEvent<HTMLElement>, selectedParam: IDropdownOption) => void;
|
||||
paramValue: string;
|
||||
selectedKey: string | number;
|
||||
}
|
||||
|
||||
export const InputParameter: FunctionComponent<InputParameterProps> = ({
|
||||
dropdownLabel,
|
||||
inputParameterTitle,
|
||||
inputLabel,
|
||||
isAddRemoveVisible,
|
||||
paramValue,
|
||||
selectedKey,
|
||||
onDeleteParamKeyPress,
|
||||
onAddNewParamKeyPress,
|
||||
onParamValueChange,
|
||||
onParamKeyChange,
|
||||
}: InputParameterProps): JSX.Element => {
|
||||
const imageProps: IImageProps = {
|
||||
width: 20,
|
||||
height: 30,
|
||||
className: dropdownLabel ? "addRemoveIconLabel" : "addRemoveIcon",
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
{inputParameterTitle && <Label>{inputParameterTitle}</Label>}
|
||||
<Stack horizontal>
|
||||
<Dropdown
|
||||
label={dropdownLabel && dropdownLabel}
|
||||
selectedKey={selectedKey}
|
||||
onChange={onParamKeyChange}
|
||||
options={options}
|
||||
styles={dropdownStyles}
|
||||
/>
|
||||
<TextField
|
||||
label={inputLabel && inputLabel}
|
||||
id="confirmCollectionId"
|
||||
autoFocus
|
||||
value={paramValue}
|
||||
onChange={onParamValueChange}
|
||||
/>
|
||||
{isAddRemoveVisible && (
|
||||
<>
|
||||
<Image
|
||||
{...imageProps}
|
||||
src={EntityCancelIcon}
|
||||
alt="Delete param"
|
||||
id="deleteparam"
|
||||
onClick={onDeleteParamKeyPress}
|
||||
/>
|
||||
<Image
|
||||
{...imageProps}
|
||||
src={AddPropertyIcon}
|
||||
alt="Add param"
|
||||
id="addparam"
|
||||
onClick={onAddNewParamKeyPress}
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
</Stack>
|
||||
</>
|
||||
);
|
||||
};
|
||||
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user