Skip to content

Commit

Permalink
Merge pull request #5 from d3i-infra/questionnaire-module
Browse files Browse the repository at this point in the history
added questionnaire-module back
  • Loading branch information
trbKnl authored Mar 6, 2024
2 parents ee20b27 + d969662 commit 159958c
Show file tree
Hide file tree
Showing 9 changed files with 438 additions and 6 deletions.
69 changes: 69 additions & 0 deletions src/framework/processing/py/port/api/props.py
Original file line number Diff line number Diff line change
Expand Up @@ -211,6 +211,74 @@ def toDict(self):
return dict


@dataclass
class PropsUIQuestionOpen:
"""
NO DOCS YET
"""
id: int
question: Translatable

def toDict(self):
dict = {}
dict["__type__"] = "PropsUIQuestionOpen"
dict["id"] = self.id
dict["question"] = self.question.toDict()
return dict


@dataclass
class PropsUIQuestionMultipleChoiceCheckbox:
"""
NO DOCS YET
"""
id: int
question: Translatable
choices: list[Translatable]

def toDict(self):
dict = {}
dict["__type__"] = "PropsUIQuestionMultipleChoiceCheckbox"
dict["id"] = self.id
dict["question"] = self.question.toDict()
dict["choices"] = [c.toDict() for c in self.choices]
return dict


@dataclass
class PropsUIQuestionMultipleChoice:
"""
NO DOCS YET
"""
id: int
question: Translatable
choices: list[Translatable]

def toDict(self):
dict = {}
dict["__type__"] = "PropsUIQuestionMultipleChoice"
dict["id"] = self.id
dict["question"] = self.question.toDict()
dict["choices"] = [c.toDict() for c in self.choices]
return dict


@dataclass
class PropsUIPromptQuestionnaire:
"""
NO DOCS YET
"""
description: Translatable
questions: list[PropsUIQuestionMultipleChoice | PropsUIQuestionMultipleChoiceCheckbox | PropsUIQuestionOpen]

def toDict(self):
dict = {}
dict["__type__"] = "PropsUIPromptQuestionnaire"
dict["description"] = self.description.toDict()
dict["questions"] = [q.toDict() for q in self.questions]
return dict


@dataclass
class PropsUIPageDonation:
"""A multi-purpose page that gets shown to the user
Expand All @@ -228,6 +296,7 @@ class PropsUIPageDonation:
| PropsUIPromptConsentForm
| PropsUIPromptFileInput
| PropsUIPromptConfirm
| PropsUIPromptQuestionnaire
)
footer: PropsUIFooter

Expand Down
32 changes: 32 additions & 0 deletions src/framework/types/elements.ts
Original file line number Diff line number Diff line change
Expand Up @@ -425,3 +425,35 @@ export interface Translatable {
export function isTranslatable(arg: any): arg is Translatable {
return isLike<Translatable>(arg, ["translations"])
}


// QUESTION ITEMS

export interface PropsUIQuestionMultipleChoice {
__type__: 'PropsUIQuestionMultipleChoice'
id: number
question: Text
choices: Text[]
}
export function isPropsUIQuestionMultipleChoice (arg: any): arg is PropsUIQuestionMultipleChoice {
return isInstanceOf<PropsUIQuestionMultipleChoice>(arg, 'PropsUIQuestionMultipleChoice', ['id', 'question', 'choices'])
}

export interface PropsUIQuestionMultipleChoiceCheckbox {
__type__: 'PropsUIQuestionMultipleChoiceCheckbox'
id: number
question: Text
choices: Text[]
}
export function isPropsUIQuestionMultipleChoiceCheckbox (arg: any): arg is PropsUIQuestionMultipleChoiceCheckbox {
return isInstanceOf<PropsUIQuestionMultipleChoiceCheckbox>(arg, 'PropsUIQuestionMultipleChoiceCheckbox', ['id', 'question', 'choices'])
}

export interface PropsUIQuestionOpen {
__type__: 'PropsUIQuestionOpen'
id: number
question: Text
}
export function isPropsUIQuestionOpen (arg: any): arg is PropsUIQuestionOpen {
return isInstanceOf<PropsUIQuestionOpen>(arg, 'PropsUIQuestionOpen', ['id', 'question'])
}
10 changes: 8 additions & 2 deletions src/framework/types/pages.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,12 @@
import { isInstanceOf } from '../helpers'
import { PropsUIHeader } from './elements'
import { PropsUIPromptFileInput, PropsUIPromptConfirm, PropsUIPromptConsentForm, PropsUIPromptRadioInput } from './prompts'
import {
PropsUIPromptFileInput,
PropsUIPromptConfirm,
PropsUIPromptConsentForm,
PropsUIPromptRadioInput,
PropsUIPromptQuestionnaire
} from './prompts'

export type PropsUIPage =
PropsUIPageSplashScreen |
Expand All @@ -22,7 +28,7 @@ export interface PropsUIPageDonation {
__type__: 'PropsUIPageDonation'
platform: string
header: PropsUIHeader
body: PropsUIPromptFileInput | PropsUIPromptConfirm | PropsUIPromptConsentForm | PropsUIPromptRadioInput
body: PropsUIPromptFileInput | PropsUIPromptConfirm | PropsUIPromptConsentForm | PropsUIPromptRadioInput | PropsUIPromptQuestionnaire
}
export function isPropsUIPageDonation (arg: any): arg is PropsUIPageDonation {
return isInstanceOf<PropsUIPageDonation>(arg, 'PropsUIPageDonation', ['platform', 'header', 'body'])
Expand Down
22 changes: 20 additions & 2 deletions src/framework/types/prompts.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
import { isInstanceOf } from "../helpers"
import { PropsUIRadioItem, Text } from "./elements"
import {
PropsUIRadioItem,
PropsUIQuestionMultipleChoice,
Text
} from './elements'

export type PropsUIPrompt =
| PropsUIPromptFileInput
Expand All @@ -8,7 +12,12 @@ export type PropsUIPrompt =
| PropsUIPromptConfirm

export function isPropsUIPrompt(arg: any): arg is PropsUIPrompt {
return isPropsUIPromptFileInput(arg) || isPropsUIPromptRadioInput(arg) || isPropsUIPromptConsentForm(arg)
return (
isPropsUIPromptFileInput(arg) ||
isPropsUIPromptRadioInput(arg) ||
isPropsUIPromptConsentForm(arg) ||
isPropsUIPromptQuestionnaire(arg)
)
}

export interface PropsUIPromptConfirm {
Expand Down Expand Up @@ -68,3 +77,12 @@ export function isPropsUIPromptConsentFormTable(arg: any): arg is PropsUIPromptC
"data_frame",
])
}

export interface PropsUIPromptQuestionnaire {
__type__: 'PropsUIPromptQuestionnaire'
questions: PropsUIQuestionMultipleChoice[]
description: Text
}
export function isPropsUIPromptQuestionnaire (arg: any): arg is PropsUIPromptQuestionnaire {
return isInstanceOf<PropsUIPromptQuestionnaire>(arg, 'PropsUIPromptQuestionnaire', ['questions', 'description'])
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
import React from 'react'
import { PropsUIQuestionMultipleChoice } from '../../../../types/elements'
import { Translator } from '../../../../translator'
import { ReactFactoryContext } from '../../factory'
import { Title3 } from './text'

interface parentSetter {
parentSetter: (arg: any) => any
}

type Props = PropsUIQuestionMultipleChoice & parentSetter & ReactFactoryContext

export const MultipleChoiceQuestion = (props: Props): JSX.Element => {
const { question, choices, id, parentSetter, locale } = props
const [selectedChoice, setSelectedChoice] = React.useState<string>("");
const [checkedArray, setCheckedArray] = React.useState(Array(choices.length).fill(false));

const copy = prepareCopy(locale)

const handleChoiceSelect = (choice: string, index: number) => {
setSelectedChoice(choice)
setCheckedArray(Array.from({ length: choices.length }, (_, i) => i === index))
};

const setParentState = () => {
parentSetter((prevState: any) => {
prevState[id] = selectedChoice
return prevState
})
}

React.useEffect(() => {
setParentState()
})

return (
<div className="p-4">
<Title3 text={copy.question}/>
<ul className="mt-4 space-y-1">
{copy.choices.map((choice, index) => (
<li key={index}>
<label className="inline-flex items-center">
<input
type="radio"
name={`${index}-${id}`}
value={choice}
checked={checkedArray.at(index)}
onChange={() => handleChoiceSelect(choice, index)}
className="mr-1 form-radio"
/>
</label>
{choice}
</li>
))}
</ul>
</div>
);

function prepareCopy (locale: string): Copy {
return {
choices: choices.map((choice) => Translator.translate(choice, locale)),
question: Translator.translate(question, locale)
}
}
}

interface Copy {
choices: string[]
question: string
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
import React from 'react'
import { PropsUIQuestionMultipleChoiceCheckbox } from '../../../../types/elements'
import { Translator } from '../../../../translator'
import { ReactFactoryContext } from '../../factory'
import {Title3 } from './text'

interface parentSetter {
parentSetter: (arg: any) => any
}

type Props = PropsUIQuestionMultipleChoiceCheckbox & parentSetter & ReactFactoryContext

export const MultipleChoiceQuestionCheckbox = (props: Props): JSX.Element => {
const { question, choices, id, parentSetter, locale } = props
const [selectedChoices, setSelectedChoices] = React.useState<string[]>([]);

const copy = prepareCopy(locale)

const setParentState = () => {
parentSetter((prevState: any) => {
prevState[id] = selectedChoices
return prevState
})
}

React.useEffect(() => {
setParentState()
})


const handleChoiceSelect = (event: React.ChangeEvent<HTMLInputElement>) => {
const { value, checked } = event.target;
if (checked) {
setSelectedChoices((prevSelectedChoices) => [
...prevSelectedChoices,
value,
]);
} else {
setSelectedChoices((prevSelectedChoices) =>
prevSelectedChoices.filter((choice) => choice !== value)
);
}
};

return (
<div className="p-4">
<Title3 text={copy.question} />
<ul className="mt-4 space-y-1">
{copy.choices.map((choice, index) => (
<li key={index}>
<label className="flex items-center">
<input
type="checkbox"
name="choice"
value={choice}
checked={selectedChoices.includes(choice)}
onChange={handleChoiceSelect}
className="mr-1 form-checkbox"
/>
{choice}
</label>
</li>
))}
</ul>
</div>
);

function prepareCopy (locale: string): Copy {
return {
choices: choices.map((choice) => Translator.translate(choice, locale)),
question: Translator.translate(question, locale)
}
}
}

interface Copy {
choices: string[]
question: string
}
56 changes: 56 additions & 0 deletions src/framework/visualisation/react/ui/elements/question_open.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
import React from 'react'
import { PropsUIQuestionOpen } from '../../../../types/elements'
import { Translator } from '../../../../translator'
import { ReactFactoryContext } from '../../factory'

import { Title3 } from './text'

interface parentSetter {
parentSetter: (arg: any) => any
}

type Props = PropsUIQuestionOpen & parentSetter & ReactFactoryContext

export const OpenQuestion = (props: Props): JSX.Element => {

const { question, id, parentSetter, locale } = props
const [userAnswer, setUserAnswer] = React.useState<string>("");
const copy = prepareCopy(locale)

const handleInputChange = (event: React.ChangeEvent<HTMLInputElement>) => {
setUserAnswer(event.target.value);
};

const setParentState = () => {
parentSetter((prevState: any) => {
prevState[id] = userAnswer
return prevState
})
}

React.useEffect(() => {
setParentState()
})

return (
<div className="p-4">
<Title3 text={copy.question} />
<input
type="text"
value={userAnswer}
onChange={handleInputChange}
className="w-full px-4 py-2 text-gray-700 bg-gray-100 border border-gray-300 rounded-md resize-none h-16"
/>
</div>
);

function prepareCopy (locale: string): Copy {
return {
question: Translator.translate(question, locale)
}
}
}

interface Copy {
question: string
}
Loading

0 comments on commit 159958c

Please sign in to comment.