Skip to content

Commit

Permalink
Configuration List Forms (#83)
Browse files Browse the repository at this point in the history
Created Lists & forms for configurations in mcs
  • Loading branch information
bexsoft committed Apr 30, 2020
1 parent b85712e commit 9df9309
Show file tree
Hide file tree
Showing 16 changed files with 1,724 additions and 731 deletions.
232 changes: 116 additions & 116 deletions portal-ui/bindata_assetfs.go

Large diffs are not rendered by default.

@@ -0,0 +1,151 @@
// This file is part of MinIO Console Server
// Copyright (c) 2019 MinIO, Inc.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
import React, {
useState,
useEffect,
createRef,
ChangeEvent,
useCallback
} from "react";
import { createStyles, Theme, withStyles } from "@material-ui/core/styles";
import Grid from "@material-ui/core/Grid";
import get from "lodash/get";
import InputBoxWrapper from "../InputBoxWrapper/InputBoxWrapper";
import { InputLabel, Tooltip } from "@material-ui/core";
import { fieldBasic } from "../common/styleLibrary";
import HelpIcon from "@material-ui/icons/Help";

interface ICSVMultiSelector {
elements: string;
name: string;
label: string;
tooltip?: string;
classes: any;
onChange: (elements: string) => void;
}

const styles = (theme: Theme) =>
createStyles({
...fieldBasic,
inputLabel: {
...fieldBasic.inputLabel,
marginBottom: 10
},
inputContainer: {
height: 150,
overflowY: "auto",
padding: 15,
position: "relative",
border: "1px solid #c4c4c4",
marginBottom: 10
},
labelContainer: {
display: "flex"
}
});

const CSVMultiSelector = ({
elements,
name,
label,
tooltip,
onChange,
classes
}: ICSVMultiSelector) => {
const [currentElements, setCurrentElements] = useState<string[]>([""]);
const bottomList = createRef<HTMLDivElement>();

// Use effect to get the initial values from props
useCallback(() => {
if (currentElements.length === 1 && currentElements[0] === "") {
const elementsSplitted = elements.split(",");
if (elementsSplitted[elementsSplitted.length - 1].trim() !== "") {
elementsSplitted.push("");
}
setCurrentElements(elementsSplitted);
}
}, [elements, setCurrentElements, currentElements]);

// Use effect to send new values to onChange
useEffect(() => {
const elementsString = currentElements
.filter(element => element.trim() !== "")
.join(",");
onChange(elementsString);
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [currentElements]);

// If the last input is not empty, we add a new one
const addEmptyLine = (elementsUp: string[]) => {
if (elementsUp[elementsUp.length - 1].trim() !== "") {
elementsUp.push("");
const refScroll = bottomList.current;

if (refScroll) {
refScroll.scrollIntoView(false);
}
}

return elementsUp;
};

// Onchange function for input box, we get the dataset-index & only update that value in the array
const onChangeElement = (e: ChangeEvent<HTMLInputElement>) => {
e.persist();

let updatedElement = [...currentElements];
const index = get(e.target, "dataset.index", 0);
updatedElement[index] = e.target.value;

updatedElement = addEmptyLine(updatedElement);
setCurrentElements(updatedElement);
};

const inputs = currentElements.map((element, index) => {
return (
<InputBoxWrapper
id={`${name}-${index.toString()}`}
label={""}
name={`${name}-${index.toString()}`}
value={currentElements[index]}
onChange={onChangeElement}
index={index}
key={`csv-${name}-${index.toString()}`}
/>
);
});

return (
<React.Fragment>
<Grid item xs={12} className={classes.fieldContainer}>
<InputLabel className={classes.inputLabel}>{label}</InputLabel>
<Grid item xs={12} className={classes.inputContainer}>
{inputs}
<div ref={bottomList} />
</Grid>
{tooltip !== "" && (
<div className={classes.tooltipContainer}>
<Tooltip title={tooltip} placement="left">
<HelpIcon />
</Tooltip>
</div>
)}
</Grid>
</React.Fragment>
);
};

export default withStyles(styles)(CSVMultiSelector);
Expand Up @@ -43,6 +43,7 @@ interface InputBoxProps {
type?: string;
tooltip?: string;
autoComplete?: string;
index?: number;
}

const styles = (theme: Theme) =>
Expand Down Expand Up @@ -89,14 +90,17 @@ const InputBoxWrapper = ({
disabled = false,
multiline = false,
tooltip = "",
index = 0,
classes
}: InputBoxProps) => {
return (
<React.Fragment>
<Grid item xs={12} className={classes.fieldContainer}>
<InputLabel htmlFor={id} className={classes.inputLabel}>
{label}
</InputLabel>
{label !== "" && (
<InputLabel htmlFor={id} className={classes.inputLabel}>
{label}
</InputLabel>
)}
<div className={classes.textBoxContainer}>
<InputField
className={classes.boxDesign}
Expand All @@ -110,10 +114,11 @@ const InputBoxWrapper = ({
type={type}
multiline={multiline}
autoComplete={autoComplete}
inputProps={{ "data-index": index }}
/>
</div>
{tooltip !== "" && (
<div>
<div className={classes.tooltipContainer}>
<Tooltip title={tooltip} placement="left">
<HelpIcon />
</Tooltip>
Expand Down
Expand Up @@ -15,6 +15,7 @@
// along with this program. If not, see <http://www.gnu.org/licenses/>.

// This object contains variables that will be used across form components.

export const fieldBasic = {
inputLabel: {
fontWeight: 500,
Expand All @@ -27,5 +28,8 @@ export const fieldBasic = {
display: "flex",
alignItems: "center",
marginBottom: 10
},
tooltipContainer: {
marginLeft: 5
}
};
161 changes: 77 additions & 84 deletions portal-ui/src/screens/Console/Configurations/ConfTargetGeneric.tsx
Expand Up @@ -19,10 +19,11 @@ import { createStyles, Theme, withStyles } from "@material-ui/core/styles";
import Grid from "@material-ui/core/Grid";
import InputBoxWrapper from "../Common/FormComponents/InputBoxWrapper/InputBoxWrapper";
import RadioGroupSelector from "../Common/FormComponents/RadioGroupSelector/RadioGroupSelector";
import { KVField } from "./types";
import { IElementValue, KVField } from "./types";
import CSVMultiSelector from "../Common/FormComponents/CSVMultiSelector/CSVMultiSelector";

interface IConfGenericProps {
onChange: (newValue: Map<string, string>) => void;
onChange: (newValue: IElementValue[]) => void;
fields: KVField[];
classes: any;
}
Expand All @@ -34,100 +35,92 @@ const ConfTargetGeneric = ({
fields,
classes
}: IConfGenericProps) => {
//Local States
const [valueHolder, setValueHolder] = useState<IElementValue[]>([]);
const fieldsElements = !fields ? [] : fields;

const [keyValues, setKeyValues] = useState<Map<string, string>>(new Map());
// Effect to create all the values to hold
useEffect(() => {
const values: IElementValue[] = [];
fields.forEach(field => {
const stateInsert: IElementValue = {
key: field.name,
value: field.type === "on|off" ? "false" : ""
};
values.push(stateInsert);
});

setValueHolder(values);
}, [fields]);

useEffect(() => {
if (keyValues.size > 0) {
onChange(keyValues);
}
}, [keyValues, onChange]);
onChange(valueHolder);
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [valueHolder]);

const setValueElement = (key: string, value: string, index: number) => {
const valuesDup = [...valueHolder];
valuesDup[index] = { key, value };

const val = (key: string): string => {
return keyValues.get(key) === undefined ? "" : keyValues.get(key) + "";
setValueHolder(valuesDup);
};
const valFall = (key: string, fallback: string): string => {
return keyValues.get(key) === undefined
? fallback
: keyValues.get(key) + "";

const fieldDefinition = (field: KVField, item: number) => {
switch (field.type) {
case "on|off":
return (
<RadioGroupSelector
currentSelection={valueHolder[item] ? valueHolder[item].value : ""}
id={field.name}
name={field.name}
label={field.label}
tooltip={field.tooltip}
onChange={(e: React.ChangeEvent<HTMLInputElement>) =>
setValueElement(field.name, e.target.value, item)
}
selectorOptions={[
{ label: "On", value: "true" },
{ label: "Off", value: "false" }
]}
/>
);
case "csv":
return (
<CSVMultiSelector
elements={valueHolder[item] ? valueHolder[item].value : ""}
label={field.label}
name={field.name}
onChange={(value: string) =>
setValueElement(field.name, value, item)
}
tooltip={field.tooltip}
/>
);
default:
return (
<InputBoxWrapper
id={field.name}
name={field.name}
label={field.label}
tooltip={field.tooltip}
value={valueHolder[item] ? valueHolder[item].value : ""}
onChange={(e: React.ChangeEvent<HTMLInputElement>) =>
setValueElement(field.name, e.target.value, item)
}
multiline={!!field.multiline}
/>
);
}
};

return (
<Grid container>
{fields.map(field => (
{fieldsElements.map((field, item) => (
<React.Fragment key={field.name}>
{field.type === "on|off" ? (
<Grid item xs={12}>
<RadioGroupSelector
currentSelection={valFall(field.name, "false")}
id={field.name}
name={field.name}
label={field.label}
tooltip={field.tooltip}
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
setKeyValues(keyValues.set(field.name, e.target.value));
}}
selectorOptions={[
{ label: "On", value: "true" },
{ label: "Off", value: "false" }
]}
/>
</Grid>
) : (
<Grid item xs={12}>
<InputBoxWrapper
id={field.name}
name={field.name}
label={field.label}
tooltip={field.tooltip}
value={val(field.name)}
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
setKeyValues(keyValues.set(field.name, e.target.value));
}}
/>
</Grid>
)}
<Grid item xs={12}>
{fieldDefinition(field, item)}
</Grid>
</React.Fragment>
))}

<Grid item xs={12}>
<InputBoxWrapper
id="queue-dir"
name="queue_dir"
label="Queue Dir"
value={val("queue_dir")}
tooltip="staging dir for undelivered messages e.g. '/home/events'"
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
setKeyValues(keyValues.set("queue_dir", e.target.value));
}}
/>
</Grid>
<Grid item xs={12}>
<InputBoxWrapper
id="queue-limit"
name="queue_limit"
label="Queue Limit"
type="number"
value={val("queue_limit")}
tooltip="maximum limit for undelivered messages, defaults to '10000'"
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
setKeyValues(keyValues.set("queue_limit", e.target.value));
}}
/>
</Grid>
<Grid item xs={12}>
<InputBoxWrapper
id="comment"
name="comment"
label="Comment"
multiline={true}
value={val("comment")}
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
setKeyValues(keyValues.set("comment", e.target.value));
}}
/>
</Grid>
<Grid item xs={12}>
<br />
</Grid>
Expand Down

0 comments on commit 9df9309

Please sign in to comment.