Add translation functionality on admin signup pages

This commit is contained in:
Aarni Halinen
2021-09-01 21:29:05 +03:00
parent 5a251f736c
commit 6bf05244c8
6 changed files with 112 additions and 76 deletions
@@ -1,49 +1,72 @@
import React from "react";
import Checkbox from "@components/Widgets/Checkbox/Checkbox";
import { SignupFormQuestion } from "@models/Signup";
import { Lang } from "src/i18n";
import {
Question, InputProps, optionTypes, SignupQuestionError,
InputProps, optionTypes, SignupQuestionError,
} from "./common";
interface OptionsWidgetProps {
inputProps: InputProps;
onChange: (value: Question[]) => void;
onChange: (value: SignupFormQuestion[]) => void;
}
class OptionsWidget extends React.Component<OptionsWidgetProps> {
handleListOptionsChange = (questions: Question[], index: number): React.ChangeEventHandler<HTMLInputElement> => (event) => {
handleListOptionsChange = (questions: SignupFormQuestion[], index: number, lang: Lang): React.ChangeEventHandler<HTMLInputElement> => (event) => {
const { onChange } = this.props;
const val = event.target.value;
const lst = val.split(";").map((p) => p.trimLeft());
// eslint-disable-next-line no-param-reassign
questions[index].options = lst;
if (lang === "fi") {
// eslint-disable-next-line no-param-reassign
questions[index].options = {
...questions[index].options,
enumNames_fi: lst,
enum: lst,
};
}
if (lang === "en") {
// eslint-disable-next-line no-param-reassign
questions[index].options = {
...questions[index].options,
enumNames_en: lst,
};
}
onChange(questions);
};
handleTextOptionsChange = (questions: Question[], index: number): React.ChangeEventHandler<HTMLInputElement> => (event) => {
handleInfoTextOptionsChange = (questions: SignupFormQuestion[], index: number, lang: Lang): React.ChangeEventHandler<HTMLInputElement> => (event) => {
const { onChange } = this.props;
const val = event.target.value;
// eslint-disable-next-line no-param-reassign
questions[index].options = val as unknown as string[]; // TODO: Check type
if (lang === "fi") {
// eslint-disable-next-line no-param-reassign
questions[index].description_fi = val;
}
if (lang === "en") {
// eslint-disable-next-line no-param-reassign
questions[index].description_en = val;
}
onChange(questions);
};
handleIntegerOptionsChange = (questions: Question[], index: number): React.ChangeEventHandler<HTMLInputElement> => (event) => {
handleIntegerOptionsChange = (questions: SignupFormQuestion[], index: number): React.ChangeEventHandler<HTMLInputElement> => (event) => {
const { onChange } = this.props;
const val = event.target.value;
if (val !== "") {
const lst = val.split(";").map((p) => p.trimLeft());
// Ignore everything else but the two first values
// eslint-disable-next-line no-param-reassign
questions[index].options = lst.splice(0, 2);
questions[index].options.enum = lst.splice(0, 2);
} else {
// eslint-disable-next-line no-param-reassign
questions[index].options = [];
questions[index].options.enum = [];
}
onChange(questions);
};
handleRequiredChange = (questions: Question[], index: number): React.ChangeEventHandler<HTMLInputElement> => (event) => {
handleRequiredChange = (questions: SignupFormQuestion[], index: number): React.ChangeEventHandler<HTMLInputElement> => (event) => {
const { onChange } = this.props;
const val: boolean = event.target.checked;
// eslint-disable-next-line no-param-reassign
@@ -67,7 +90,7 @@ class OptionsWidget extends React.Component<OptionsWidgetProps> {
render(): JSX.Element {
const { inputProps } = this.props;
const {
type, value, questions, index,
value, type, questions, index,
} = inputProps;
if (!optionTypes.includes(type)) {
throw new SignupQuestionError(`Question widget type "${type}" not in types array.`);
@@ -82,25 +105,29 @@ class OptionsWidget extends React.Component<OptionsWidgetProps> {
<>
<input
type="text"
placeholder="Write something informative"
value={questions[index].options}
onChange={this.handleTextOptionsChange(questions, index)}
placeholder="Write something informative in Finnish"
value={questions[index].description_fi}
onChange={this.handleInfoTextOptionsChange(questions, index, "fi")}
required
/>
<input
type="text"
placeholder="Write something informative in English"
value={questions[index].description_en}
onChange={this.handleInfoTextOptionsChange(questions, index, "en")}
required
/>
{this.requiredField()}
</>
);
}
if (type === "integer") {
const lst = value as string[];
const joinedValue = lst.join(";");
return (
<>
<input
type="text"
placeholder="Minimum;Maximum"
value={joinedValue}
value={value.enum.join(";")}
onChange={this.handleIntegerOptionsChange(questions, index)}
/>
{this.requiredField()}
@@ -109,15 +136,20 @@ class OptionsWidget extends React.Component<OptionsWidgetProps> {
}
if (type === "radiobutton") {
const lst = value as string[];
const joinedValue = lst.join(";");
return (
<>
<input
type="text"
placeholder="Kyllä;ei;ehkä"
value={value.enumNames_fi.join(";")}
onChange={this.handleListOptionsChange(questions, index, "fi")}
required
/>
<input
type="text"
placeholder="Yes;no;maybe"
value={joinedValue}
onChange={this.handleListOptionsChange(questions, index)}
value={value.enumNames_en.join(";")}
onChange={this.handleListOptionsChange(questions, index, "en")}
required
/>
</>
@@ -125,15 +157,20 @@ class OptionsWidget extends React.Component<OptionsWidgetProps> {
}
if (type === "checkbox") {
const lst = value as string[];
const joinedValue = lst.join(";");
return (
<>
<input
type="text"
placeholder="A;B;C"
value={joinedValue}
onChange={this.handleListOptionsChange(questions, index)}
placeholder="Yksi;Kaksi;Kolme"
value={value.enumNames_fi.join(";")}
onChange={this.handleListOptionsChange(questions, index, "fi")}
required
/>
<input
type="text"
placeholder="One;Two;Three"
value={value.enumNames_en.join(";")}
onChange={this.handleListOptionsChange(questions, index, "en")}
required
/>
{this.requiredField()}
@@ -2,7 +2,8 @@ import React, { ReactNode } from "react";
import styled from "styled-components";
import { Draggable } from "react-beautiful-dnd";
import colors from "@theme/colors";
import { Question, InputProps } from "./common";
import { SignupFormQuestion } from "@models/Signup";
import { Lang } from "src/i18n";
import OptionsWidget from "./OptionsWidget";
import TypeWidget from "./TypeWidget";
import QuestionElement from "./Question";
@@ -16,26 +17,28 @@ const WidgetRow = styled.div`
`;
interface QuestionListProps {
questions: Question[];
questions: SignupFormQuestion[];
innerRef: React.Ref<HTMLDivElement>;
placeholder: ReactNode;
onChange: (value: Question[]) => void;
onChange: (value: SignupFormQuestion[]) => void;
}
class QuestionList extends React.Component<QuestionListProps> {
renderTextWidget = ({ questions, value, index }: InputProps): JSX.Element => (
<input type="text" value={value} onChange={this.handleNameInputChange(questions, index)} />
);
handleNameInputChange = (questions: Question[], index: number): React.ChangeEventHandler<HTMLInputElement> => (event) => {
handleNameInputChange = (questions: SignupFormQuestion[], index: number, lang: Lang): React.ChangeEventHandler<HTMLInputElement> => (event) => {
const { onChange } = this.props;
const val = event.target.value;
// eslint-disable-next-line no-param-reassign
questions[index].name = val;
if (lang === "fi") {
// eslint-disable-next-line no-param-reassign
questions[index].title_fi = val;
}
if (lang === "en") {
// eslint-disable-next-line no-param-reassign
questions[index].title_en = val;
}
onChange(questions);
};
handleElementRemove = (questions: Question[], index: number) => (): void => {
handleElementRemove = (questions: SignupFormQuestion[], index: number) => (): void => {
const { onChange } = this.props;
const newQuestions = [...questions];
newQuestions.splice(index, 1);
@@ -45,11 +48,6 @@ class QuestionList extends React.Component<QuestionListProps> {
renderQuestions(): JSX.Element[] {
const { questions, onChange } = this.props;
return questions.map((q, index) => {
const nameWidgetProps = {
value: q.name, type: "text", questions, index,
};
const nameWidget = this.renderTextWidget(nameWidgetProps);
const dataProps = {
value: q.options, type: q.type, questions, index,
};
@@ -66,7 +64,8 @@ class QuestionList extends React.Component<QuestionListProps> {
<QuestionElement
onClick={this.handleElementRemove(questions, index)}
>
{nameWidget}
<input type="text" value={q.title_fi} onChange={this.handleNameInputChange(questions, index, "fi")} />
<input type="text" value={q.title_en} onChange={this.handleNameInputChange(questions, index, "en")} />
{typeSelectWidget}
{optionsWidget}
</QuestionElement>
@@ -4,8 +4,8 @@ import shortid from "shortid";
import { DragDropContext, Droppable } from "react-beautiful-dnd";
import colors from "@theme/colors";
import AddIcon from "@components/AddIcon";
import { SignupFormQuestion } from "@models/Signup";
import QuestionList from "./QuestionList";
import { Question } from "./common";
const Widget = styled.div`
& > button {
@@ -40,24 +40,25 @@ interface SignupQuestionsWidgetProps {
}
const SignupQuestionsWidget: React.FC<SignupQuestionsWidgetProps> = ({ value, onFocus, onChange }) => {
const onValueChange = (questions: Question[]) => {
const onValueChange = (questions: SignupFormQuestion[]) => {
const newValue = JSON.stringify(questions);
onChange(newValue);
};
const handleNewRowClick = (questions) => () => {
const newRow: Question = {
const newRow: SignupFormQuestion = {
id: shortid.generate(),
name: `Question #${questions.length + 1}`,
options: [],
title_fi: `Question #${questions.length + 1}`,
title_en: `Question #${questions.length + 1}`,
options: undefined,
type: "text",
};
const newQuestions: Question[] = questions.concat([newRow]);
const newQuestions: SignupFormQuestion[] = questions.concat([newRow]);
onValueChange(newQuestions);
};
const handleDragEnd = (questions: Question[]) => (result) => {
const handleDragEnd = (questions: SignupFormQuestion[]) => (result) => {
const srcIndex = result.source.index;
const dstIndex = result.destination.index;
const srcCopy = { ...questions[srcIndex] };
@@ -66,7 +67,7 @@ const SignupQuestionsWidget: React.FC<SignupQuestionsWidgetProps> = ({ value, on
onValueChange(questions);
};
const questions = JSON.parse(value) as Question[];
const questions: SignupFormQuestion[] = JSON.parse(value);
return (
<Widget>
@@ -1,32 +1,29 @@
import React from "react";
import { Question, InputProps, optionTypes } from "./common";
import { SignupFormQuestion } from "@models/Signup";
import { InputProps, optionTypes } from "./common";
interface TypeWidgetProps {
inputProps: InputProps;
onChange: (value: Question[]) => void;
onChange: (value: SignupFormQuestion[]) => void;
}
class TypeWidget extends React.Component<TypeWidgetProps> {
handleTypeChange = (questions: Question[], index: number): React.ChangeEventHandler<HTMLSelectElement> => (event) => {
const { onChange } = this.props;
const val = event.target.value as Question["type"];
const TypeWidget = ({ onChange, inputProps }: TypeWidgetProps): JSX.Element => {
const handleTypeChange = (questions: SignupFormQuestion[], index: number): React.ChangeEventHandler<HTMLSelectElement> => (event) => {
const val = event.target.value as SignupFormQuestion["type"];
// eslint-disable-next-line no-param-reassign
questions[index].type = val;
onChange(questions);
};
render(): JSX.Element {
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>
);
}
}
const { questions, type, index } = inputProps;
const options = optionTypes.map((t) => (
<option key={t} value={t}>{t}</option>
));
return (
<select onChange={handleTypeChange(questions, index)} value={type} name="type">
{options}
</select>
);
};
export default TypeWidget;
@@ -1,3 +1,5 @@
import type { SignupFormQuestion } from "@models/Signup";
export interface Question {
id: string;
name: string;
@@ -10,8 +12,8 @@ export interface Question {
export interface InputProps {
index: number;
value: string | string[];
questions: Question[];
value: SignupFormQuestion["options"];
questions: SignupFormQuestion[];
type: string;
}