add KeyValueInput

This commit is contained in:
Sidney Alcantara
2021-11-10 20:16:13 +11:00
parent 858d08f6dc
commit 64a68bf4cd
2 changed files with 153 additions and 0 deletions

View File

@@ -0,0 +1,137 @@
import { useState } from "react";
import {
FormControl,
FormLabel,
FormGroup,
Stack,
TextField,
ButtonGroup,
Button,
} from "@mui/material";
import AddIcon from "@mui/icons-material/Add";
import RemoveIcon from "@mui/icons-material/Remove";
export interface IKeyValueInputProps {
value: Record<string, string>;
onChange: (value: Record<string, string>) => void;
label?: React.ReactNode;
}
export default function KeyValueInput({
value: valueProp,
onChange,
label,
}: IKeyValueInputProps) {
const [value, setValue] = useState(
Object.keys(valueProp).length > 0
? Object.keys(valueProp)
.sort()
.map((key) => [key, valueProp[key]])
: [["", ""]]
);
const saveValue = (v: typeof value) => {
onChange(
v.reduce((acc, [key, value]) => {
if (key.length > 0) acc[key] = value;
return acc;
}, {} as Record<string, string>)
);
};
const handleAdd = (i: number) => () =>
setValue((v) => {
const newValue = [...v];
newValue.splice(i + 1, 0, ["", ""]);
setTimeout(() =>
document.getElementById(`keyValue-${i + 1}-key`)?.focus()
);
return newValue;
});
const handleRemove = (i: number) => () =>
setValue((v) => {
const newValue = [...v];
newValue.splice(i, 1);
saveValue(newValue);
return newValue;
});
const handleChange =
(i: number, j: number) => (e: React.ChangeEvent<HTMLInputElement>) =>
setValue((v) => {
const newValue = [...v];
newValue[i][j] = e.target.value;
saveValue(newValue);
return newValue;
});
return (
<FormControl variant="filled">
<FormLabel
component="legend"
sx={{ typography: "button", color: "text.primary", mb: 0.25, ml: 0.25 }}
>
{label}
</FormLabel>
<FormGroup>
{value.map(([propKey, propValue], i) => (
<Stack
key={i}
direction="row"
alignItems="flex-start"
sx={{ "& + &": { mt: 1 } }}
>
<TextField
id={`keyValue-${i}-key`}
aria-label="Key"
placeholder="Key"
value={propKey}
sx={{
"& .MuiInputBase-root": {
borderTopRightRadius: 0,
borderBottomRightRadius: 0,
},
}}
onChange={handleChange(i, 0)}
error={propKey.length === 0}
helperText={propKey.length === 0 ? "Required" : ""}
/>
<TextField
id={`keyValue-${i}-value`}
aria-label="Value"
placeholder="Value"
value={propValue}
sx={{
ml: "-1px",
"& .MuiInputBase-root": {
borderTopLeftRadius: 0,
borderBottomLeftRadius: 0,
},
}}
onChange={handleChange(i, 1)}
/>
<ButtonGroup color="secondary" sx={{ ml: 1 }}>
<Button
onClick={handleAdd(i)}
aria-label="Add row"
style={{ minWidth: 32, paddingLeft: 0, paddingRight: 0 }}
>
<AddIcon style={{ fontSize: 18 }} />
</Button>
<Button
onClick={handleRemove(i)}
aria-label="Remove row"
style={{ minWidth: 32, paddingLeft: 0, paddingRight: 0 }}
>
<RemoveIcon style={{ fontSize: 18 }} />
</Button>
</ButtonGroup>
</Stack>
))}
</FormGroup>
</FormControl>
);
}

View File

@@ -22,6 +22,7 @@ import {
import Navigation from "@src/components/Navigation";
import CodeEditor from "@src/components/CodeEditor";
import ReactJson from "react-json-view";
import KeyValueInput from "@src/components/KeyValueInput";
// import { useConfirmation } from "@src/components/ConfirmationDialog";
import { useProjectContext } from "@src/contexts/ProjectContext";
@@ -44,6 +45,11 @@ export default function TestView() {
const [method, setMethod] = useState<"GET" | "POST" | "DELETE">("GET");
const [path, setPath] = useState<string>("/");
const [codeValid, setCodeValid] = useState(true);
const [headers, setHeaders] = useState<Record<string, string>>({
x: "1",
y: "2",
z: "3",
});
const cachedBodyKey = path.replace("/", "");
@@ -212,6 +218,16 @@ export default function TestView() {
</FormControl>
)}
{/* TODO: Remove */}
<KeyValueInput
value={headers}
onChange={(v) => setHeaders(v)}
label="Headers"
/>
<code style={{ display: "block" }}>
{JSON.stringify(headers, undefined, 2)}
</code>
<InputLabel sx={{ mb: 1, mt: 4 }}>Response</InputLabel>
<Paper sx={{ p: 2 }}>
<ReactJson