mirror of
https://github.com/Azure/cosmos-explorer.git
synced 2025-03-25 11:16:03 +00:00
* added SettingsV2 Tab * lint changes * foxed failing test * Addressed PR comments - removed dangerouslySetInnerHtml - removed underscore dependency - added AccessibleElement - removed unnecesary exceptions to linting * split render into separate functions - removed sinon in test - Added some enums to replace constant strings - removed dangerously set inner html - made autopilot input as StatefulValue * add settingscomponent snapshot * fixed linting errors * fixed errors * addressed PR comments - Moved StatefulValue to new class - Split render to more functions for throughputInputComponents * Added sub components - Added tests for SettingsRenderUtls - Added empty test files for adding tests later * Moved all inputs to fluent UI - removed rupm - added reusable styles * Added Tabs - Added ToolTipLabel component - Removed toggleables for individual components - Removed accessible elements - Added IndexingPolicyComponent * Added more tests * Addressed PR comments * Moved Label radio buttons to choicegroup * fixed lint errors * Removed StatefulValue - Moved conflict res tab to the end - Added styling for autpilot radiobuttons * fixed linting errors * fix bugs from merge to master * fixed formatting issue * Addressed PR comments - Added unit tests for smaller methods within each component * fixed linting errors * removed redundant snapshots * removed empty line * made separate props objects for subcomponents * Moved dirty checks to sub components * Made indesing policy component height = 80% of view port - modified auto pilot v3 messages - Added Fluent UI tolltip - * Moved warning messages inline * moved conflict res helpers out * fixed bugs * added stack style for message * fixed tests * Added tests * fixed linting and format errors * undid changes * more edits * fixed compile errors * fixed compile errors * fixed errors * fixed bug with save and discard buttons * fixed compile errors * addressed PR comments
260 lines
10 KiB
TypeScript
260 lines
10 KiB
TypeScript
import * as React from "react";
|
|
import { Stack } from "office-ui-fabric-react/lib/Stack";
|
|
import { Dropdown, IDropdownOption, IDropdownStyles } from "office-ui-fabric-react/lib/Dropdown";
|
|
import { Checkbox } from "office-ui-fabric-react/lib/Checkbox";
|
|
import { TextField, ITextFieldStyles } from "office-ui-fabric-react/lib/TextField";
|
|
import { DefaultButton } from "office-ui-fabric-react";
|
|
import "./FeaturePanelComponent.less";
|
|
|
|
export const FeaturePanelComponent: React.FunctionComponent = () => {
|
|
// Initial conditions
|
|
const originalParams = new URLSearchParams(window.location.search);
|
|
const urlParams = new Map(); // Params with lowercase keys
|
|
originalParams.forEach((value: string, key: string) => urlParams.set(key.toLocaleLowerCase(), value));
|
|
|
|
const baseUrlOptions = [
|
|
{ key: "https://localhost:1234/explorer.html", text: "localhost:1234" },
|
|
{ key: "https://cosmos.azure.com/explorer.html", text: "cosmos.azure.com" },
|
|
{ key: "https://portal.azure.com", text: "portal" }
|
|
];
|
|
|
|
const platformOptions = [
|
|
{ key: "Hosted", text: "Hosted" },
|
|
{ key: "Portal", text: "Portal" },
|
|
{ key: "Emulator", text: "Emulator" },
|
|
{ key: "", text: "None" }
|
|
];
|
|
|
|
// React hooks to keep state
|
|
const [baseUrl, setBaseUrl] = React.useState<IDropdownOption>(
|
|
baseUrlOptions.find(o => o.key === window.location.origin + window.location.pathname) || baseUrlOptions[0]
|
|
);
|
|
const [platform, setPlatform] = React.useState<IDropdownOption>(
|
|
urlParams.has("platform")
|
|
? platformOptions.find(o => o.key === urlParams.get("platform")) || platformOptions[0]
|
|
: platformOptions[0]
|
|
);
|
|
|
|
const booleanFeatures: {
|
|
key: string;
|
|
label: string;
|
|
value: string;
|
|
disabled?: () => boolean;
|
|
reactState?: [boolean, React.Dispatch<React.SetStateAction<boolean>>];
|
|
onChange?: (_?: React.FormEvent<HTMLElement | HTMLInputElement>, checked?: boolean) => void;
|
|
}[] = [
|
|
{ key: "feature.enablechangefeedpolicy", label: "Enable change feed policy", value: "true" },
|
|
{ key: "feature.enablerupm", label: "Enable RUPM", value: "true" },
|
|
{ key: "feature.dataexplorerexecutesproc", label: "Execute stored procedure", value: "true" },
|
|
{ key: "feature.hosteddataexplorerenabled", label: "Hosted Data Explorer (deprecated?)", value: "true" },
|
|
{ key: "feature.enablettl", label: "Enable TTL", value: "true" },
|
|
{ key: "feature.enablegallerypublish", label: "Enable Notebook Gallery Publishing", value: "true" },
|
|
{ key: "feature.enablecodeofconduct", label: "Enable Code Of Conduct Acknowledgement", value: "true" },
|
|
{
|
|
key: "feature.enableLinkInjection",
|
|
label: "Enable Injecting Notebook Viewer Link into the first cell",
|
|
value: "true"
|
|
},
|
|
{ key: "feature.enablesettingsv2", label: "Enable SettingsV2 Tab", value: "true" },
|
|
{ key: "feature.canexceedmaximumvalue", label: "Can exceed max value", value: "true" },
|
|
{
|
|
key: "feature.enablefixedcollectionwithsharedthroughput",
|
|
label: "Enable fixed collection with shared throughput",
|
|
value: "true"
|
|
},
|
|
{ key: "feature.ttl90days", label: "TTL 90 days", value: "true" },
|
|
{ key: "feature.enablenotebooks", label: "Enable notebooks", value: "true" },
|
|
{
|
|
key: "feature.customportal",
|
|
label: "Force Production portal (portal only)",
|
|
value: "false",
|
|
disabled: (): boolean => baseUrl.key !== "https://portal.azure.com"
|
|
},
|
|
{ key: "feature.enablespark", label: "Enable Synapse", value: "true" },
|
|
{ key: "feature.enableautopilotv2", label: "Enable Auto-pilot V2", value: "true" }
|
|
];
|
|
|
|
const stringFeatures: {
|
|
key: string;
|
|
label: string;
|
|
placeholder: string;
|
|
disabled?: () => boolean;
|
|
reactState?: [string, React.Dispatch<React.SetStateAction<string>>];
|
|
onChange?: (_: React.FormEvent<HTMLInputElement | HTMLTextAreaElement>, newValue?: string) => void;
|
|
}[] = [
|
|
{ key: "feature.notebookserverurl", label: "Notebook server URL", placeholder: "https://notebookserver" },
|
|
{ key: "feature.notebookservertoken", label: "Notebook server token", placeholder: "" },
|
|
{ key: "feature.notebookbasepath", label: "Notebook base path", placeholder: "" },
|
|
{ key: "key", label: "Auth key", placeholder: "" },
|
|
{
|
|
key: "dataExplorerSource",
|
|
label: "Data Explorer Source (portal only)",
|
|
placeholder: "https://localhost:1234/explorer.html",
|
|
disabled: (): boolean => baseUrl.key !== "https://portal.azure.com"
|
|
},
|
|
{ key: "feature.livyendpoint", label: "Livy endpoint", placeholder: "" }
|
|
];
|
|
|
|
booleanFeatures.forEach(
|
|
f => (f.reactState = React.useState<boolean>(urlParams.has(f.key) ? urlParams.get(f.key) === "true" : false))
|
|
);
|
|
stringFeatures.forEach(
|
|
f => (f.reactState = React.useState<string>(urlParams.has(f.key) ? urlParams.get(f.key) : undefined))
|
|
);
|
|
|
|
const buildUrl = (): string => {
|
|
const fragments = (platform.key === "" ? [] : [`platform=${platform.key}`])
|
|
.concat(booleanFeatures.map(f => (f.reactState[0] ? `${f.key}=${f.value}` : "")))
|
|
.concat(stringFeatures.map(f => (f.reactState[0] ? `${f.key}=${encodeURIComponent(f.reactState[0])}` : "")))
|
|
.filter(v => v && v.length > 0);
|
|
|
|
const paramString = fragments.length < 1 ? "" : `?${fragments.join("&")}`;
|
|
return `${baseUrl.key}${paramString}`;
|
|
};
|
|
|
|
const onChangeBaseUrl = (event: React.FormEvent<HTMLDivElement>, option?: IDropdownOption): void => {
|
|
setBaseUrl(option);
|
|
};
|
|
|
|
const onChangePlatform = (event: React.FormEvent<HTMLDivElement>, option?: IDropdownOption): void => {
|
|
setPlatform(option);
|
|
};
|
|
|
|
booleanFeatures.forEach(
|
|
f =>
|
|
(f.onChange = (ev?: React.FormEvent<HTMLElement | HTMLInputElement>, checked?: boolean): void => {
|
|
f.reactState[1](checked);
|
|
})
|
|
);
|
|
|
|
stringFeatures.forEach(
|
|
f =>
|
|
(f.onChange = (event: React.FormEvent<HTMLInputElement | HTMLTextAreaElement>, newValue?: string): void => {
|
|
f.reactState[1](newValue);
|
|
})
|
|
);
|
|
|
|
const onNotebookShortcut = (): void => {
|
|
booleanFeatures.find(f => f.key === "feature.enablenotebooks").reactState[1](true);
|
|
stringFeatures
|
|
.find(f => f.key === "feature.notebookserverurl")
|
|
.reactState[1]("https://localhost:10001/12345/notebook/");
|
|
stringFeatures.find(f => f.key === "feature.notebookservertoken").reactState[1]("token");
|
|
stringFeatures.find(f => f.key === "feature.notebookbasepath").reactState[1]("./notebooks");
|
|
setPlatform(platformOptions.find(o => o.key === "Hosted"));
|
|
};
|
|
|
|
const onPortalLocalDEShortcut = (): void => {
|
|
setBaseUrl(baseUrlOptions.find(o => o.key === "https://portal.azure.com"));
|
|
setPlatform(platformOptions.find(o => o.key === "Portal"));
|
|
stringFeatures.find(f => f.key === "dataExplorerSource").reactState[1]("https://localhost:1234/explorer.html");
|
|
};
|
|
|
|
const onReset = (): void => {
|
|
booleanFeatures.forEach(f => f.reactState[1](false));
|
|
stringFeatures.forEach(f => f.reactState[1](""));
|
|
};
|
|
|
|
const stackTokens = { childrenGap: 10 };
|
|
const dropdownStyles: Partial<IDropdownStyles> = { dropdown: { width: 200 } };
|
|
const textFieldStyles: Partial<ITextFieldStyles> = { fieldGroup: { width: 300 } };
|
|
|
|
// Show in 2 columns to keep it compact
|
|
let halfSize = Math.ceil(booleanFeatures.length / 2);
|
|
const leftBooleanFeatures = booleanFeatures.slice(0, halfSize);
|
|
const rightBooleanFeatures = booleanFeatures.slice(halfSize, booleanFeatures.length);
|
|
|
|
halfSize = Math.ceil(stringFeatures.length / 2);
|
|
const leftStringFeatures = stringFeatures.slice(0, halfSize);
|
|
const rightStringFeatures = stringFeatures.slice(halfSize, stringFeatures.length);
|
|
|
|
const anchorOptions = {
|
|
href: buildUrl(),
|
|
target: "_blank",
|
|
rel: "noopener"
|
|
};
|
|
|
|
return (
|
|
<div className="featurePanelComponentContainer">
|
|
<div className="urlContainer">
|
|
<a {...anchorOptions}>{buildUrl()}</a>
|
|
</div>
|
|
<Stack className="options" tokens={stackTokens}>
|
|
<Stack horizontal horizontalAlign="space-between" tokens={stackTokens}>
|
|
<DefaultButton onClick={onNotebookShortcut}>Notebooks on localhost</DefaultButton>
|
|
<DefaultButton onClick={onPortalLocalDEShortcut}>Portal points to local DE</DefaultButton>
|
|
<DefaultButton onClick={onReset}>Reset</DefaultButton>
|
|
</Stack>
|
|
<Stack horizontal horizontalAlign="start" tokens={stackTokens}>
|
|
<Dropdown
|
|
selectedKey={baseUrl.key}
|
|
options={baseUrlOptions}
|
|
onChange={onChangeBaseUrl}
|
|
label="Base Url"
|
|
styles={dropdownStyles}
|
|
/>
|
|
<Dropdown
|
|
label="Platform"
|
|
selectedKey={platform.key}
|
|
onChange={onChangePlatform}
|
|
options={platformOptions}
|
|
styles={dropdownStyles}
|
|
/>
|
|
</Stack>
|
|
<Stack horizontal>
|
|
<Stack className="checkboxRow" horizontalAlign="space-between">
|
|
{leftBooleanFeatures.map(f => (
|
|
<Checkbox
|
|
key={f.key}
|
|
label={f.label}
|
|
checked={f.reactState[0]}
|
|
onChange={f.onChange}
|
|
disabled={f.disabled && f.disabled()}
|
|
/>
|
|
))}
|
|
</Stack>
|
|
<Stack className="checkboxRow" horizontalAlign="space-between">
|
|
{rightBooleanFeatures.map(f => (
|
|
<Checkbox
|
|
key={f.key}
|
|
label={f.label}
|
|
checked={f.reactState[0]}
|
|
onChange={f.onChange}
|
|
disabled={f.disabled && f.disabled()}
|
|
/>
|
|
))}
|
|
</Stack>
|
|
</Stack>
|
|
<Stack horizontal tokens={stackTokens}>
|
|
<Stack horizontalAlign="space-between">
|
|
{leftStringFeatures.map(f => (
|
|
<TextField
|
|
key={f.key}
|
|
value={f.reactState[0]}
|
|
label={f.label}
|
|
onChange={f.onChange}
|
|
styles={textFieldStyles}
|
|
placeholder={f.placeholder}
|
|
disabled={f.disabled && f.disabled()}
|
|
/>
|
|
))}
|
|
</Stack>
|
|
<Stack horizontalAlign="space-between">
|
|
{rightStringFeatures.map(f => (
|
|
<TextField
|
|
key={f.key}
|
|
value={f.reactState[0]}
|
|
label={f.label}
|
|
onChange={f.onChange}
|
|
styles={textFieldStyles}
|
|
placeholder={f.placeholder}
|
|
disabled={f.disabled && f.disabled()}
|
|
/>
|
|
))}
|
|
</Stack>
|
|
</Stack>
|
|
</Stack>
|
|
</div>
|
|
);
|
|
};
|