Coursemology/coursemology2

View on GitHub
client/app/bundles/course/survey/containers/ResponseForm/ResponseAnswer.jsx

Summary

Maintainability
A
1 hr
Test Coverage
import { Controller } from 'react-hook-form';
import { defineMessages, injectIntl } from 'react-intl';
import PropTypes from 'prop-types';

import { questionTypes } from 'course/survey/constants';
import FormTextField from 'lib/components/form/fields/TextField';
import formTranslations from 'lib/translations/form';

import MultipleChoiceOptionsField from './components/MultipleChoiceOptionsField';
import MultipleResponseOptionsField from './components/MultipleResponseOptionsField';

const responseFormTranslations = defineMessages({
  selectAtLeast: {
    id: 'course.survey.ResponseForm.ResponseAnswer.selectAtLeast',
    defaultMessage: 'Please select at least {count} option(s).',
  },
  selectAtMost: {
    id: 'course.survey.ResponseForm.ResponseAnswer.selectAtMost',
    defaultMessage: 'Please select at most {count} option(s).',
  },
});

const checkTextResponseRequired = (value, question, intl) =>
  question.required && !value
    ? intl.formatMessage(formTranslations.required)
    : true;

const checkMultipleChoiceRequired = (value, question, intl) =>
  question.required && (!value || value.length < 1)
    ? intl.formatMessage(responseFormTranslations.selectAtLeast, { count: 1 })
    : true;

const checkQuantitySelected = (options, question, intl) => {
  const { required, max_options: maxOptions } = question;
  const minOptions = question.min_options || 1;
  const optionCount = options.length;
  // Skip checks if question is not required and student doesn't intend to answer it.
  if (!required && optionCount === 0) {
    return true;
  }

  if (minOptions && optionCount < minOptions) {
    return intl.formatMessage(responseFormTranslations.selectAtLeast, {
      count: minOptions,
    });
  }
  if (maxOptions && optionCount > maxOptions) {
    return intl.formatMessage(responseFormTranslations.selectAtMost, {
      count: maxOptions,
    });
  }

  return true;
};

const renderTextResponseField = (props) => {
  const { control, disabled, intl, question, questionIndex, sectionIndex } =
    props;

  return (
    <Controller
      control={control}
      name={`sections.${sectionIndex}.questions.${questionIndex}.answer.text_response`}
      render={({ field, fieldState }) => (
        <FormTextField
          disabled={disabled}
          field={field}
          fieldState={fieldState}
          fullWidth
          InputLabelProps={{
            shrink: true,
          }}
          multiline
          variant="standard"
        />
      )}
      rules={{ validate: (v) => checkTextResponseRequired(v, question, intl) }}
    />
  );
};

const renderMultipleChoiceField = (props) => {
  const { control, disabled, intl, question, questionIndex, sectionIndex } =
    props;

  return (
    <Controller
      control={control}
      name={`sections.${sectionIndex}.questions.${questionIndex}.answer.question_option_ids`}
      render={({ field, fieldState }) => (
        <MultipleChoiceOptionsField
          disabled={disabled}
          field={field}
          fieldState={fieldState}
          question={question}
        />
      )}
      rules={{
        validate: (v) => checkMultipleChoiceRequired(v, question, intl),
      }}
    />
  );
};

const renderMultipleResponseField = (props) => {
  const { control, disabled, intl, question, questionIndex, sectionIndex } =
    props;

  return (
    <Controller
      control={control}
      name={`sections.${sectionIndex}.questions.${questionIndex}.answer.question_option_ids`}
      render={({ field, fieldState }) => (
        <MultipleResponseOptionsField
          disabled={disabled}
          field={field}
          fieldState={fieldState}
          question={question}
        />
      )}
      rules={{
        validate: (v) => checkQuantitySelected(v, question, intl),
      }}
    />
  );
};

const ResponseAnswer = (props) => {
  const { TEXT, MULTIPLE_CHOICE, MULTIPLE_RESPONSE } = questionTypes;
  const { control, disabled, intl, question, questionIndex, sectionIndex } =
    props;
  if (!question) {
    return <div />;
  }
  const renderer = {
    [TEXT]: renderTextResponseField({
      control,
      disabled,
      intl,
      question,
      questionIndex,
      sectionIndex,
    }),
    [MULTIPLE_CHOICE]: renderMultipleChoiceField({
      control,
      disabled,
      intl,
      question,
      questionIndex,
      sectionIndex,
    }),
    [MULTIPLE_RESPONSE]: renderMultipleResponseField({
      control,
      disabled,
      intl,
      question,
      questionIndex,
      sectionIndex,
    }),
  }[question.question_type];
  if (!renderer) {
    return <div />;
  }
  return renderer;
};

ResponseAnswer.propTypes = {
  control: PropTypes.object.isRequired,
  disabled: PropTypes.bool.isRequired,
  intl: PropTypes.object,
  question: PropTypes.object,
  questionIndex: PropTypes.number.isRequired,
  sectionIndex: PropTypes.number.isRequired,
};

export default injectIntl(ResponseAnswer);