cosmos-explorer/src/Explorer/Controls/FeaturePanel/FeaturePanelComponent.tsx

260 lines
10 KiB
TypeScript
Raw Normal View History

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"
},
Refactored Settings Tab (#161) * 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
2020-09-30 12:34:39 -07:00
{ 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");
2020-06-26 16:52:28 +02:00
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>
);
};