Skip to content

Commit

Permalink
Merge pull request #2980 from kobotoolbox/master-fix-submission-modal…
Browse files Browse the repository at this point in the history
…-choices-bug

Fix submission modal displaying wrong labels for choices
  • Loading branch information
jnm authored Feb 12, 2021
2 parents 5343e60 + 08d9499 commit 74874f7
Show file tree
Hide file tree
Showing 5 changed files with 412 additions and 335 deletions.
58 changes: 36 additions & 22 deletions jsapp/js/components/submissionDataTable.es6
Original file line number Diff line number Diff line change
Expand Up @@ -2,19 +2,19 @@ import React from 'react';
import autoBind from 'react-autobind';
import {
formatTimeDate,
formatDate
formatDate,
} from 'utils';
import {bem} from 'js/bem';
import {renderTypeIcon} from 'js/assetUtils';
import {
DISPLAY_GROUP_TYPES,
getSubmissionDisplayData
getSubmissionDisplayData,
} from 'js/submissionUtils';
import {
META_QUESTION_TYPES,
QUESTION_TYPES,
SCORE_ROW_TYPE,
RANK_LEVEL_TYPE
RANK_LEVEL_TYPE,
} from 'js/constants';

/**
Expand Down Expand Up @@ -103,7 +103,7 @@ class SubmissionDataTable extends React.Component {
</bem.SubmissionDataTable__column>

<bem.SubmissionDataTable__column m='data'>
{this.renderResponseData(item.type, item.data)}
{this.renderResponseData(item.type, item.data, item.listName)}
</bem.SubmissionDataTable__column>
</bem.SubmissionDataTable__row>
);
Expand All @@ -112,8 +112,9 @@ class SubmissionDataTable extends React.Component {
/**
* @prop {string} type
* @prop {string|null} data
* @prop {string|undefined} listName
*/
renderResponseData(type, data) {
renderResponseData(type, data, listName) {
if (data === null) {
return null;
}
Expand All @@ -124,24 +125,36 @@ class SubmissionDataTable extends React.Component {
case QUESTION_TYPES.get('select_one').id:
case SCORE_ROW_TYPE:
case RANK_LEVEL_TYPE:
choice = this.findChoice(data);
return (
<bem.SubmissionDataTable__value>
{choice.label[this.props.translationIndex]}
</bem.SubmissionDataTable__value>
);
choice = this.findChoice(listName, data);
if (!choice) {
console.error(`Choice not found for "${listName}" and "${data}".`);
// fallback to raw data to display anything meaningful
return data;
} else {
return (
<bem.SubmissionDataTable__value>
{choice.label[this.props.translationIndex] || choice.name}
</bem.SubmissionDataTable__value>
);
}
case QUESTION_TYPES.get('select_multiple').id:
return (
<ul>
{data.split(' ').map((answer, answerIndex) => {
choice = this.findChoice(answer);
return (
<li key={answerIndex}>
<bem.SubmissionDataTable__value>
{choice.label[this.props.translationIndex]}
</bem.SubmissionDataTable__value>
</li>
);
choice = this.findChoice(listName, answer);
if (!choice) {
console.error(`Choice not found for "${listName}" and "${answer}".`);
// fallback to raw data to display anything meaningful
return answer;
} else {
return (
<li key={answerIndex}>
<bem.SubmissionDataTable__value>
{choice.label[this.props.translationIndex] || choice.name}
</bem.SubmissionDataTable__value>
</li>
);
}
})}
</ul>
);
Expand Down Expand Up @@ -179,12 +192,13 @@ class SubmissionDataTable extends React.Component {
}

/**
* @prop {string} name
* @prop {string} listName
* @prop {string} choiceName
* @returns {object|undefined}
*/
findChoice(name) {
findChoice(listName, choiceName) {
return this.props.asset.content.choices.find((choice) => {
return choice.name === name;
return choice.name === choiceName && choice.list_name === listName;
});
}

Expand Down
12 changes: 10 additions & 2 deletions jsapp/js/constants.es6
Original file line number Diff line number Diff line change
Expand Up @@ -380,9 +380,16 @@ export const SCORE_ROW_TYPE = 'score__row';
// a custom question type for rank
export const RANK_LEVEL_TYPE = 'rank__level';

export const CHOICE_LISTS = Object.freeze({
SELECT: 'select_from_list_name',
MATRIX: 'kobo--matrix_list',
SCORE: 'kobo--score-choices',
RANK: 'kobo--rank-items',
});

export const MATRIX_PAIR_PROPS = {
inSurvey: 'kobo--matrix_list',
inChoices: 'list_name'
inSurvey: CHOICE_LISTS.MATRIX,
inChoices: 'list_name',
};

export const NAME_MAX_LENGTH = 255;
Expand Down Expand Up @@ -417,6 +424,7 @@ const constants = {
RANK_LEVEL_TYPE,
NAME_MAX_LENGTH,
CATEGORY_LABELS,
CHOICE_LISTS,
};

export default constants;
42 changes: 37 additions & 5 deletions jsapp/js/submissionUtils.es6
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,16 @@ import {
getRowName,
getTranslatedRowLabel,
getSurveyFlatPaths,
isRowSpecialLabelHolder
isRowSpecialLabelHolder,
} from 'js/assetUtils';
import {
FORM_VERSION_NAME,
SCORE_ROW_TYPE,
RANK_LEVEL_TYPE,
MATRIX_PAIR_PROPS,
GROUP_TYPES_BEGIN,
QUESTION_TYPES
QUESTION_TYPES,
CHOICE_LISTS,
} from 'js/constants';

export const DISPLAY_GROUP_TYPES = new Map();
Expand All @@ -19,7 +20,7 @@ new Set([
'group_repeat',
'group_regular',
'group_matrix',
'group_matrix_row'
'group_matrix_row',
]).forEach((codename) => {DISPLAY_GROUP_TYPES.set(codename, codename);});

/**
Expand Down Expand Up @@ -50,14 +51,20 @@ class DisplayGroup {
* @property {string} type - One of QUESTION_TYPES
* @property {string} label - Localized display label
* @property {string} name - Unique identifier
* @property {string|undefined} listName - Unique identifier of a choices list,
* only applicable for question types
* that uses choices lists
* @property {string|null} data - User response, `null` for no response
*/
class DisplayResponse {
constructor(type, label, name, data = null) {
constructor(type, label, name, listName, data = null) {
this.type = type;
this.label = label;
this.name = name;
this.data = data;
if (listName) {
this.listName = listName;
}
}
}

Expand Down Expand Up @@ -85,6 +92,7 @@ export function getSubmissionDisplayData(survey, choices, translationIndex, subm
const row = survey[rowIndex];

const rowName = getRowName(row);
let rowListName = getRowListName(row);
const rowLabel = getTranslatedRowLabel(rowName, survey, translationIndex);

let parentGroupPath = null;
Expand Down Expand Up @@ -194,10 +202,20 @@ export function getSubmissionDisplayData(survey, choices, translationIndex, subm
rowData = rowData[repeatIndex];
}

// score and rank don't have list name on them and they need to use
// the one of their parent
if (row.type === SCORE_ROW_TYPE || row.type === RANK_LEVEL_TYPE) {
const parentGroupRow = survey.find((row) => {
return getRowName(row) === parentGroup.name;
});
rowListName = getRowListName(parentGroupRow);
}

let rowObj = new DisplayResponse(
row.type,
rowLabel,
rowName,
rowListName,
rowData
);
parentGroup.addChild(rowObj);
Expand Down Expand Up @@ -276,6 +294,7 @@ function populateMatrixData(
questionSurveyObj.type,
getTranslatedRowLabel(questionName, survey, translationIndex),
questionName,
getRowListName(questionSurveyObj),
questionData
);
matrixRowGroupObj.addChild(questionObj);
Expand Down Expand Up @@ -379,8 +398,21 @@ function getRegularGroupAnswers(data, targetKey) {
return answers;
}

/**
* @param {object} row
* @returns {string|undefiend}
*/
function getRowListName(row) {
return (
row[CHOICE_LISTS.SELECT] ||
row[CHOICE_LISTS.MATRIX] ||
row[CHOICE_LISTS.SCORE] ||
row[CHOICE_LISTS.RANK]
);
}

export default {
DISPLAY_GROUP_TYPES,
getSubmissionDisplayData,
getRepeatGroupAnswers
getRepeatGroupAnswers,
};
Loading

0 comments on commit 74874f7

Please sign in to comment.