Skip to content

Instantly share code, notes, and snippets.

@rotimi-best
Created June 2, 2021 13:29
Show Gist options
  • Save rotimi-best/88cf56e5b7d083e61f837a64becb0f3b to your computer and use it in GitHub Desktop.
Save rotimi-best/88cf56e5b7d083e61f837a64becb0f3b to your computer and use it in GitHub Desktop.
Questionnaire Builder
<div id="root"></div>
const {
AppBar,
CssBaseline,
Typography,
Toolbar,
Card,
CardActions,
CardContent,
Icon,
IconButton,
Button,
makeStyles,
TextField,
FormControl,
InputLabel,
Select,
MenuItem,
Tooltip,
Fab,
Grid
} = MaterialUI;
const toolTipUseStyles = makeStyles((theme) => ({
absolute: {
position: 'fixed',
bottom: theme.spacing(2),
right: theme.spacing(3),
},
}));
function SimpleTooltips({ handleAdd }) {
const classes = toolTipUseStyles();
return (
<Tooltip title="Add New Question" aria-label="add" onClick={handleAdd}>
<Fab color="secondary" className={classes.absolute}>
<Icon>add</Icon>
</Fab>
</Tooltip>
);
}
const headerUseStyles = makeStyles((theme) => ({
root: {
flexGrow: 1,
},
title: {
flexGrow: 1,
},
}));
function Header() {
const classes = headerUseStyles();
return (
<div className={classes.root}>
<AppBar>
<Toolbar>
<IconButton edge="start" color="default" aria-label="menu">
<Icon>menu</Icon>
</IconButton>
<Typography variant="h6" className={classes.title}>
Questionnaire Builder
</Typography>
<Button color="inherit">Preview</Button>
</Toolbar>
</AppBar>
<Toolbar />
<Toolbar />
</div>
);
}
const questioHeadernUseStyles = makeStyles({
root: {
width: '100%',
},
text: {
width: '100%'
},
});
function QuestionnaireHeader() {
const classes = questioHeadernUseStyles();
const [state, setState] = React.useState({
cancerType: '',
uniqueId: ''
});
const handleChange = (type) => e => {
setState({
...state,
[type]: e.target.value
})
};
return (
<Card className={classes.root}>
<CardContent>
<TextField
id="standard-basic"
label="Cancer Type"
value={state.cancerType}
className={classes.text}
onChange={handleChange('cancerType')}
/>
<TextField
id="standard-basic"
label="Unique ID"
value={state.uniqueId}
className={classes.text}
onChange={handleChange('uniqueId')}
/>
</CardContent>
</Card>
)
}
const conditionUseStyles = makeStyles(theme => ({
root: {
width: '100%',
marginTop: 18
},
title: {
marginTop: 20
},
formControl: {
margin: theme.spacing(1),
minWidth: 160,
// width: '100%'
},
}));
const conditionOptions = {
questions: ["Question 1", "Question 2", "Question 3"],
condition: ["Contains one of", "Equal to", "Doensn't contain"],
options: ["Option 1", "Option 2", "Option 3"]
}
const Condition = props => {
const classes = conditionUseStyles();
const [state, setState] = React.useState({
questions: '',
condition: '',
options: ''
})
const handleChange = (key) => (e) => {
setState({
...state,
[key]: e.target.value
});
};
return (
<div className={classes.root}>
<Typography component="h6" variant="h6" className={classes.title}>
Condition <Fab color="primary" size="small">
<Icon>add</Icon>
</Fab>
</Typography>
<Grid container>
<Grid item xs={4}>
<FormControl className={classes.formControl}>
<InputLabel id="select-question-label">Question</InputLabel>
<Select
labelId="select-question-label"
id="select-question"
value={state.questions}
onChange={handleChange('questions')}
>
{conditionOptions.questions.map(q => <MenuItem value={q}>{q}</MenuItem>)}
</Select>
</FormControl>
</Grid>
<Grid item xs={4}>
<FormControl className={classes.formControl}>
<InputLabel id="select-question-label">Condition</InputLabel>
<Select
labelId="select-condition-label"
id="select-condition"
value={state.condition}
onChange={handleChange('condition')}
>
{conditionOptions.condition.map(q => <MenuItem value={q}>{q}</MenuItem>)}
</Select>
</FormControl>
</Grid>
<Grid item xs={4}>
<FormControl className={classes.formControl}>
<InputLabel id="select-question-label">Options</InputLabel>
<Select
labelId="select-options-label"
id="select-options"
value={state.options}
onChange={handleChange('options')}
>
{conditionOptions.options.map(q => <MenuItem value={q}>{q}</MenuItem>)}
</Select>
</FormControl>
</Grid>
</Grid>
</div>
)
}
const radioUseStyles = makeStyles(theme => ({
root: {
width: '100%',
marginTop: 12
},
optionTitle: {
marginTop: 20
},
text: {
width: '100%',
display: 'flex',
alignItems: 'center',
height: '100%',
justifyContent: 'center'
},
closeBtn: {
display: 'flex',
alignItems: 'center',
justifyContent: 'center'
},
input: {
width: '100%'
},
}));
const Option = ({ classes, option, index, handleChange, handleRemove }) => (
<React.Fragment>
<Grid item xs={1}>
<Typography component="p" variant="subtitle2" className={classes.text}>
{index + 1}.
</Typography>
</Grid>
<Grid item xs={10}>
<TextField
id="standard-basic"
label={`Label ${index + 1}`}
value={option.label}
// key="label"
className={classes.input}
onChange={handleChange(index, 'label')}
/>
<TextField
id="standard-basic"
label={`Value ${index + 1}`}
value={option.value}
className={classes.input}
onChange={handleChange(index, 'value')}
/>
{/**/}
</Grid>
<Grid item xs={1} className={classes.closeBtn}>
<IconButton
edge="start"
color="default"
aria-label="menu"
onClick={handleRemove(index)}
>
<Icon>close</Icon>
</IconButton>
</Grid>
</React.Fragment>
)
function RadioOptions() {
const classes = radioUseStyles();
const [options, setOptions] = React.useState([]);
const handleOptionChange = (index, key) => (e) => {
const updatedOptions = [...options];
updatedOptions[index] = {
...updatedOptions[index],
[key]: e.target.value
};
setOptions(updatedOptions)
}
const handleFocus = () => {
setOptions(options => ([
...options,
{
label: '',
value: ''
}
]));
}
const handleOptionRemove = (index) => () => {
setOptions(options => options.filter((option, i) => i !== index))
}
const AddOption = () => (
<>
<Grid item xs={10}>
<TextField
label="Add option"
onFocus={handleFocus}
/>
</Grid>
</>
)
return (
<div>
<Typography component="h6" variant="h6" className={classes.optionTitle}>
Options
</Typography>
<Grid container justify="center" spacing={1}>
{options.map((option, i) =>
<Option
key={`option_${i}`}
classes={classes}
option={option}
index={i}
handleChange={handleOptionChange}
handleRemove={handleOptionRemove}
/>
)}
<AddOption />
</Grid>
</div>
)
}
const questionTemplateUseStyles = makeStyles(theme => ({
root: {
width: '100%',
marginTop: 18,
position: 'relative'
},
text: {
width: '100%'
},
formControl: {
marginTop: theme.spacing(1),
minWidth: 200,
},
button: {
position: 'absolute',
top: 0,
right: 0
}
}));
function QuestionTemplate(props) {
const {
handleDelete,
index,
question
} = props;
const classes = questionTemplateUseStyles();
const [state, setState] = React.useState({
...question
});
const handleChange = (type) => e => {
setState({
...state,
[type]: e.target.value
})
};
return (
<Card className={classes.root}>
<IconButton
className={classes.button}
onClick={() => {handleDelete(question._id)}}
>
<Icon>close</Icon>
</IconButton>
<CardContent>
<TextField
label="Question ID"
value={state.qId}
className={classes.text}
onChange={handleChange('qId')}
/>
<TextField
label="ID"
value={state.id}
className={classes.text}
onChange={handleChange('id')}
/>
<TextField
label="Title"
value={state.title}
className={classes.text}
onChange={handleChange('title')}
/>
<FormControl variant="outlined" className={classes.formControl}>
<InputLabel id="demo-simple-select-outlined-label">Option</InputLabel>
<Select
labelId="question-type"
value={state.type}
onChange={handleChange('type')}
label="Type"
>
<MenuItem value="radio">Radio</MenuItem>
<MenuItem value="checkbox">Checkbox</MenuItem>
<MenuItem value="number">Number</MenuItem>
</Select>
</FormControl>
{["radio", "checkbox"].includes(state.type) && <RadioOptions />}
<Condition />
</CardContent>
<CardActions>
{/*
<Button
variant="contained"
color="primary"
className={classes.button}
startIcon={<Icon>add</Icon>}
>
Add parameter
</Button>
*/}
</CardActions>
</Card>
)
}
const defaultQuestionProp = (_id = 0) => ({
_id,
qId: '',
id: '',
title: '',
subtitle: '',
type: '',
})
const questionUseStyles = makeStyles({
root: {
minWidth: 250,
width: 770,
margin: 'auto',
paddingBottom: 64
}
});
function Questionnaire() {
const classes = questionUseStyles();
const [questions, setQuestions] = React.useState([defaultQuestionProp()]);
const handleDelete = (_id) => {
console.log('_id', _id)
setQuestions([
...questions.filter((q) => q._id !== _id)
])
}
const handleAdd = () => {
setQuestions([
...questions,
defaultQuestionProp(questions.length)
])
}
return (
<div className={classes.root}>
<QuestionnaireHeader />
{questions.map((q) => <QuestionTemplate key={`key-${q._id}`} question={q} handleDelete={handleDelete}/>)}
<SimpleTooltips handleAdd={handleAdd} />
</div>
)
}
const App = () => {
return (
<div className="">
<CssBaseline />
<Header />
<Questionnaire />
</div>
)
}
ReactDOM.render(
<App />,
document.getElementById('root')
);
<script src="https://unpkg.com/react/umd/react.development.js"></script>
<script src="https://unpkg.com/react-dom/umd/react-dom.development.js"></script>
<script src="https://unpkg.com/@material-ui/core@latest/umd/material-ui.development.js"></script>
<script src="https://unpkg.com/babel-standalone@latest/babel.min.js"></script>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment