Restructure signup questions widget
This commit is contained in:
@@ -0,0 +1,46 @@
|
|||||||
|
import * as React from "react";
|
||||||
|
import * as shortid from "shortid";
|
||||||
|
import { Question, InputProps, optionTypes, SignupQuestionError } from "./index";
|
||||||
|
|
||||||
|
export interface OptionsWidgetProps {
|
||||||
|
inputProps: InputProps;
|
||||||
|
onChange: (value: Question[]) => void;
|
||||||
|
}
|
||||||
|
export interface OptionsWidgetState { }
|
||||||
|
|
||||||
|
class OptionsWidget extends React.Component<OptionsWidgetProps, OptionsWidgetState> {
|
||||||
|
handleRadiobuttonOptionsChange = (questions: Question[], index: number) => (event) => {
|
||||||
|
const { onChange } = this.props;
|
||||||
|
const val = event.target.value;
|
||||||
|
const lst = val.split(",").map(p => p.trimLeft());
|
||||||
|
questions[index].options = lst;
|
||||||
|
onChange(questions);
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
const { inputProps } = this.props;
|
||||||
|
const { type, value, questions, index } = inputProps;
|
||||||
|
if (!optionTypes.includes(type)) {
|
||||||
|
throw new SignupQuestionError(`Qquestion widget type "${type}" not in types array.`);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (type === "text") {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
else if (type === "radiobutton") {
|
||||||
|
const lst = value as string[];
|
||||||
|
const joinedValue = lst.join(",");
|
||||||
|
return <input
|
||||||
|
type="text"
|
||||||
|
placeholder="Yes,no,maybe"
|
||||||
|
value={joinedValue}
|
||||||
|
onChange={this.handleRadiobuttonOptionsChange(questions, index)} />;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
throw new SignupQuestionError(`Unrecognized question widget type "${type}"`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default OptionsWidget;
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
@import "../../assets/scss/globals";
|
||||||
|
|
||||||
|
.signup-questions-widget {
|
||||||
|
button.add-link {
|
||||||
|
background: none;
|
||||||
|
padding: 0;
|
||||||
|
margin: 1rem 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.signup-questions-widget-row {
|
||||||
|
margin-bottom: 1rem;
|
||||||
|
display: flex;
|
||||||
|
flex-flow: column nowrap;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,5 +1,11 @@
|
|||||||
import * as React from "react";
|
import * as React from "react";
|
||||||
import * as shortid from "shortid";
|
import * as shortid from "shortid";
|
||||||
|
import OptionsWidget from "./OptionsWidget";
|
||||||
|
import { Question, InputProps, optionTypes } from ".";
|
||||||
|
// @ts-ignore
|
||||||
|
import * as AddIcon from "../../assets/img/add-icon.png";
|
||||||
|
import "./SignupQuestionsWidget.scss";
|
||||||
|
import TypeWidget from "./TypeWidget";
|
||||||
|
|
||||||
export interface SignupQuestionsWidgetProps {
|
export interface SignupQuestionsWidgetProps {
|
||||||
value: string;
|
value: string;
|
||||||
@@ -9,27 +15,6 @@ export interface SignupQuestionsWidgetProps {
|
|||||||
}
|
}
|
||||||
export interface SignupQuestionsWidgetState {}
|
export interface SignupQuestionsWidgetState {}
|
||||||
|
|
||||||
interface Question {
|
|
||||||
id: string;
|
|
||||||
name: string;
|
|
||||||
type: string;
|
|
||||||
options: string[];
|
|
||||||
}
|
|
||||||
|
|
||||||
interface InputProps {
|
|
||||||
index: number;
|
|
||||||
value: string | string[];
|
|
||||||
questions: Question[];
|
|
||||||
type: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
const types = [
|
|
||||||
"text",
|
|
||||||
"radiobutton",
|
|
||||||
];
|
|
||||||
|
|
||||||
class SignupQuestionError extends Error {}
|
|
||||||
|
|
||||||
class SignupQuestionsWidget extends React.Component<SignupQuestionsWidgetProps, SignupQuestionsWidgetState> {
|
class SignupQuestionsWidget extends React.Component<SignupQuestionsWidgetProps, SignupQuestionsWidgetState> {
|
||||||
onValueChange = (questions: Question[]) => {
|
onValueChange = (questions: Question[]) => {
|
||||||
const { onChange } = this.props;
|
const { onChange } = this.props;
|
||||||
@@ -55,67 +40,18 @@ class SignupQuestionsWidget extends React.Component<SignupQuestionsWidgetProps,
|
|||||||
this.onValueChange(questions);
|
this.onValueChange(questions);
|
||||||
}
|
}
|
||||||
|
|
||||||
handleTypeChange = (questions: Question[], index: number) => (event) => {
|
|
||||||
const val = event.target.value;
|
|
||||||
questions[index].type = val;
|
|
||||||
this.onValueChange(questions);
|
|
||||||
}
|
|
||||||
|
|
||||||
handleRadiobuttonOptionsChange = (questions: Question[], index: number) => (event) => {
|
|
||||||
const val = event.target.value;
|
|
||||||
const lst = val.split(",").map(p => p.trimLeft());
|
|
||||||
questions[index].options = lst;
|
|
||||||
this.onValueChange(questions);
|
|
||||||
}
|
|
||||||
|
|
||||||
renderTextWidget = ({ questions, value, index }: InputProps) => (
|
renderTextWidget = ({ questions, value, index }: InputProps) => (
|
||||||
<input type="text" value={value} onChange={this.handleNameInputChange(questions, index)} />
|
<input type="text" value={value} onChange={this.handleNameInputChange(questions, index)} />
|
||||||
)
|
)
|
||||||
|
|
||||||
renderOptionsWidgetByType = (props: InputProps) => {
|
|
||||||
const { type, value, questions, index } = props;
|
|
||||||
if (!types.includes(type)) {
|
|
||||||
throw new SignupQuestionError(`Qquestion widget type "${type}" not in types array.`);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (type === "text") {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
else if (type === "radiobutton") {
|
|
||||||
const lst = value as string[];
|
|
||||||
const joinedValue = lst.join(",");
|
|
||||||
return <input
|
|
||||||
type="text"
|
|
||||||
placeholder="Yes,no,maybe"
|
|
||||||
value={joinedValue}
|
|
||||||
onChange={this.handleRadiobuttonOptionsChange(questions, index)} />;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
throw new SignupQuestionError(`Unrecognized question widget type "${type}"`);
|
|
||||||
}
|
|
||||||
|
|
||||||
renderTypeSelectWidget = (props: InputProps) => {
|
|
||||||
const { questions, index, type } = props;
|
|
||||||
const options = types.map(t => (
|
|
||||||
<option key={t} value={t}>{t}</option>
|
|
||||||
));
|
|
||||||
return (
|
|
||||||
<select onChange={this.handleTypeChange(questions, index)} value={type} name="type">
|
|
||||||
{ options }
|
|
||||||
</select>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
renderQuestions = (questions: Question[]) => {
|
renderQuestions = (questions: Question[]) => {
|
||||||
return questions.map((q, index) => {
|
return questions.map((q, index) => {
|
||||||
const nameWidgetProps = { value: q.name, type: "text", questions, index };
|
const nameWidgetProps = { value: q.name, type: "text", questions, index };
|
||||||
const nameWidget = this.renderTextWidget(nameWidgetProps);
|
const nameWidget = this.renderTextWidget(nameWidgetProps);
|
||||||
|
|
||||||
const optionsWidgetProps = { value: q.options, type: q.type, questions, index };
|
const dataProps = { value: q.options, type: q.type, questions, index };
|
||||||
const optionsWidget = this.renderOptionsWidgetByType(optionsWidgetProps);
|
const optionsWidget = <OptionsWidget inputProps={dataProps} onChange={this.onValueChange} />;
|
||||||
const typeSelectWidget = this.renderTypeSelectWidget(optionsWidgetProps);
|
const typeSelectWidget = <TypeWidget inputProps={dataProps} onChange={this.onValueChange} />;
|
||||||
return (
|
return (
|
||||||
<div key={q.id} className="signup-questions-widget-row">
|
<div key={q.id} className="signup-questions-widget-row">
|
||||||
{ nameWidget }
|
{ nameWidget }
|
||||||
@@ -133,8 +69,10 @@ class SignupQuestionsWidget extends React.Component<SignupQuestionsWidgetProps,
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="signup-questions-widget">
|
<div className="signup-questions-widget">
|
||||||
<button type="button" onClick={this.handleNewRowClick(questions)}>New Row</button>
|
|
||||||
{this.renderQuestions(questions)}
|
{this.renderQuestions(questions)}
|
||||||
|
<button type="button" className="add-link" onClick={this.handleNewRowClick(questions)}>
|
||||||
|
<img src={AddIcon} /> New Question
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,33 @@
|
|||||||
|
import * as React from "react";
|
||||||
|
import * as shortid from "shortid";
|
||||||
|
import { Question, InputProps, optionTypes, SignupQuestionError } from "./index";
|
||||||
|
|
||||||
|
export interface TypeWidgetProps {
|
||||||
|
inputProps: InputProps;
|
||||||
|
onChange: (value: Question[]) => void;
|
||||||
|
}
|
||||||
|
export interface TypeWidgetState { }
|
||||||
|
|
||||||
|
class TypeWidget extends React.Component<TypeWidgetProps, TypeWidgetState> {
|
||||||
|
handleTypeChange = (questions: Question[], index: number) => (event) => {
|
||||||
|
const { onChange } = this.props;
|
||||||
|
const val = event.target.value;
|
||||||
|
questions[index].type = val;
|
||||||
|
onChange(questions);
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
const { inputProps } = this.props;
|
||||||
|
const { type, questions, index } = inputProps;
|
||||||
|
const options = optionTypes.map(t => (
|
||||||
|
<option key={t} value={t}>{t}</option>
|
||||||
|
));
|
||||||
|
return (
|
||||||
|
<select onChange={this.handleTypeChange(questions, index)} value={type} name="type">
|
||||||
|
{options}
|
||||||
|
</select>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default TypeWidget;
|
||||||
@@ -1,2 +1,24 @@
|
|||||||
import SignupQuestionsWidget from "./SignupQuestionsWidget";
|
import SignupQuestionsWidget from "./SignupQuestionsWidget";
|
||||||
|
|
||||||
|
export interface Question {
|
||||||
|
id: string;
|
||||||
|
name: string;
|
||||||
|
type: string;
|
||||||
|
options: string[];
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface InputProps {
|
||||||
|
index: number;
|
||||||
|
value: string | string[];
|
||||||
|
questions: Question[];
|
||||||
|
type: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const optionTypes = [
|
||||||
|
"text",
|
||||||
|
"radiobutton",
|
||||||
|
];
|
||||||
|
|
||||||
|
export class SignupQuestionError extends Error { }
|
||||||
|
|
||||||
export default SignupQuestionsWidget;
|
export default SignupQuestionsWidget;
|
||||||
|
|||||||
@@ -20,7 +20,8 @@
|
|||||||
margin-bottom: 0.5rem;
|
margin-bottom: 0.5rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
input {
|
input,
|
||||||
|
select {
|
||||||
padding: 0.3rem 0.5rem;
|
padding: 0.3rem 0.5rem;
|
||||||
margin-bottom: 0.5rem;
|
margin-bottom: 0.5rem;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user