diff --git a/src/constants/dictionary.ts b/src/constants/dictionary.ts
index 83c77bf8f..d15bbcc5c 100644
--- a/src/constants/dictionary.ts
+++ b/src/constants/dictionary.ts
@@ -515,6 +515,10 @@ const dictionary: Dictionary = {
fr: 'Retrouver dans le questionnaire',
en: 'Retrieve in the questionnaire',
},
+ allowArbitraryResponse: {
+ fr: 'Autoriser une réponse libre',
+ en: 'Allow an arbitrary response',
+ },
list: {
fr: 'Liste',
en: 'List',
diff --git a/src/model/formToState/component-new-edit/collected-variable.jsx b/src/model/formToState/component-new-edit/collected-variable.jsx
index abaa4b46f..aafbd92d5 100644
--- a/src/model/formToState/component-new-edit/collected-variable.jsx
+++ b/src/model/formToState/component-new-edit/collected-variable.jsx
@@ -47,6 +47,7 @@ export function formToState(form) {
y,
z,
mesureLevel,
+ arbitraryVariableOfVariableId,
codeListReference,
codeListReferenceLabel,
isCollected,
@@ -61,6 +62,7 @@ export function formToState(form) {
x,
y,
z,
+ arbitraryVariableOfVariableId,
isCollected,
alternativeLabel,
mesureLevel,
@@ -94,6 +96,7 @@ export function storeToForm(currentStore) {
x,
y,
z,
+ arbitraryVariableOfVariableId,
isCollected = '1',
alternativeLabel,
mesureLevel,
@@ -107,6 +110,7 @@ export function storeToForm(currentStore) {
x,
y,
z,
+ arbitraryVariableOfVariableId,
isCollected,
alternativeLabel,
mesureLevel,
diff --git a/src/model/formToState/component-new-edit/response-format-single.jsx b/src/model/formToState/component-new-edit/response-format-single.jsx
index 08436f739..7cf3acb61 100644
--- a/src/model/formToState/component-new-edit/response-format-single.jsx
+++ b/src/model/formToState/component-new-edit/response-format-single.jsx
@@ -10,12 +10,14 @@ import {
const { RADIO } = DATATYPE_VIS_HINT;
export const defaultState = {
+ allowArbitraryResponse: false,
mandatory: false,
visHint: RADIO,
// [DEFAULT_CODES_LIST_SELECTOR_PATH]: cloneDeep(CodesListDefaultState),
};
export const defaultForm = {
+ allowArbitraryResponse: false,
mandatory: false,
visHint: RADIO,
// [DEFAULT_CODES_LIST_SELECTOR_PATH]: cloneDeep(CodesListDefaultForm),
@@ -24,6 +26,7 @@ export const defaultForm = {
export function formToState(form, transformers) {
const {
id,
+ allowArbitraryResponse,
mandatory,
visHint,
[DEFAULT_CODES_LIST_SELECTOR_PATH]: codesListForm,
@@ -31,6 +34,7 @@ export function formToState(form, transformers) {
return {
id,
+ allowArbitraryResponse,
mandatory,
visHint,
[DEFAULT_CODES_LIST_SELECTOR_PATH]:
@@ -39,10 +43,11 @@ export function formToState(form, transformers) {
}
export function stateToForm(currentState, transformers) {
- const { id, visHint, mandatory } = currentState;
+ const { id, allowArbitraryResponse, visHint, mandatory } = currentState;
return {
id,
+ allowArbitraryResponse,
mandatory,
visHint,
[DEFAULT_CODES_LIST_SELECTOR_PATH]:
diff --git a/src/model/formToState/component-new-edit/response-format-table.jsx b/src/model/formToState/component-new-edit/response-format-table.jsx
index f43dc3df0..6b14f61ac 100644
--- a/src/model/formToState/component-new-edit/response-format-table.jsx
+++ b/src/model/formToState/component-new-edit/response-format-table.jsx
@@ -75,6 +75,7 @@ export const defaultMeasureForm = {
type: SIMPLE,
[SIMPLE]: defaultMeasureSimpleState,
[SINGLE_CHOICE]: {
+ allowArbitraryResponse: false,
[DEFAULT_CODES_LIST_SELECTOR_PATH]: cloneDeep(CodesListDefaultForm),
visHint: RADIO,
},
diff --git a/src/model/remote-to-stores.jsx b/src/model/remote-to-stores.jsx
index 59f7a0162..186dc9dd4 100644
--- a/src/model/remote-to-stores.jsx
+++ b/src/model/remote-to-stores.jsx
@@ -44,6 +44,12 @@ export function questionnaireRemoteToStores(remote, currentStores = {}) {
remote.Child,
collectedVariables,
);
+
+ const arbitraryVariables = Component.getArbitraryVariablesFromRemote(
+ remote.Child,
+ collectedVariables,
+ );
+
const codesListsStore = CodesList.remoteToStore(
codesLists,
variableclarification,
@@ -56,6 +62,7 @@ export function questionnaireRemoteToStores(remote, currentStores = {}) {
responsesByVariable,
codesListsStore,
variableclarification,
+ arbitraryVariables,
),
};
// Components store
diff --git a/src/model/transformations/collected-variable.jsx b/src/model/transformations/collected-variable.jsx
index 33277d005..2b7ab3e81 100644
--- a/src/model/transformations/collected-variable.jsx
+++ b/src/model/transformations/collected-variable.jsx
@@ -20,6 +20,7 @@ export function remoteToStore(
responsesByVariable,
codesListsStore,
variableclarification,
+ arbitraryVariables,
) {
remote.forEach((variable) => {
if (variableclarification) {
@@ -45,9 +46,26 @@ export function remoteToStore(
}
}
}
+ if (arbitraryVariables) {
+ const extendedArbitraryVariable = arbitraryVariables.find(
+ (arbitraryVariable) =>
+ arbitraryVariable.CollectedVariableReference === variable.id,
+ );
+ if (extendedArbitraryVariable) {
+ variable.arbitraryVariableOfVariableId =
+ extendedArbitraryVariable.arbitraryVariableOfVariableId;
+ }
+ }
});
return remote.reduce((acc, ev) => {
- const { Name: name, Label: label, CodeListReference, z, mesureLevel } = ev;
+ const {
+ Name: name,
+ Label: label,
+ CodeListReference,
+ z,
+ mesureLevel,
+ arbitraryVariableOfVariableId,
+ } = ev;
const id = ev.id || uuid();
const formatSingleRemote = remoteToStateFormatSimple({
@@ -69,6 +87,7 @@ export function remoteToStore(
...responsesByVariable[id],
z,
mesureLevel,
+ arbitraryVariableOfVariableId,
},
};
}, {});
diff --git a/src/model/transformations/collected-variable.spec.jsx b/src/model/transformations/collected-variable.spec.jsx
index b636126dd..cd2218cb3 100644
--- a/src/model/transformations/collected-variable.spec.jsx
+++ b/src/model/transformations/collected-variable.spec.jsx
@@ -218,6 +218,86 @@ describe('collected variable tranformations', () => {
output,
);
});
+
+ test('should add the arbitrary variables to the collected', () => {
+ const input = [
+ {
+ id: 'm6aty8by',
+ Name: 'SUGGESTER',
+ type: 'CollectedVariableType',
+ Label: 'SUGGESTER label',
+ Datatype: {
+ type: 'TextDatatypeType',
+ Pattern: '',
+ typeName: 'TEXT',
+ MaxLength: 1,
+ },
+ CodeListReference: 'id',
+ },
+ {
+ id: 'm6atzjnb',
+ Name: 'SUGGESTER_ARBITRARY',
+ type: 'CollectedVariableType',
+ Label: 'SUGGESTER_ARBITRARY label',
+ Datatype: {
+ type: 'TextDatatypeType',
+ typeName: 'TEXT',
+ MaxLength: 249,
+ },
+ arbitraryVariableOfVariableId: 'm6aty8by',
+ },
+ ];
+ const arbitraryVariables = [
+ {
+ Datatype: {
+ type: 'TextDatatypeType',
+ typeName: 'TEXT',
+ MaxLength: 249,
+ },
+ mandatory: false,
+ CollectedVariableReference: 'm6atzjnb',
+ arbitraryVariableOfVariableId: 'm6aty8by',
+ },
+ ];
+ const responsesByVariable = { m6aty8by: {} };
+ const codesListStore = { id: { label: 'label' } };
+ const variableclarification = [];
+ const output = {
+ m6aty8by: {
+ id: 'm6aty8by',
+ name: 'SUGGESTER',
+ label: 'SUGGESTER label',
+ type: TEXT,
+ codeListReference: 'id',
+ codeListReferenceLabel: 'label',
+ TEXT: {
+ maxLength: 1,
+ pattern: '',
+ },
+ },
+ m6atzjnb: {
+ id: 'm6atzjnb',
+ name: 'SUGGESTER_ARBITRARY',
+ label: 'SUGGESTER_ARBITRARY label',
+ type: TEXT,
+ codeListReferenceLabel: '',
+ TEXT: {
+ maxLength: 249,
+ },
+ arbitraryVariableOfVariableId: 'm6aty8by',
+ },
+ };
+
+ expect(
+ remoteToStore(
+ input,
+ responsesByVariable,
+ codesListStore,
+ variableclarification,
+ arbitraryVariables,
+ ),
+ ).toEqual(output);
+ });
});
describe('remoteToComponentState', () => {
test('should return the state representation of a collected variable', () => {
diff --git a/src/model/transformations/component.jsx b/src/model/transformations/component.jsx
index 7822ec03b..e0ee412e1 100644
--- a/src/model/transformations/component.jsx
+++ b/src/model/transformations/component.jsx
@@ -174,6 +174,61 @@ export const getClarificarionfromremote = (Children, collectedVariables) => {
return variableClarification;
};
+/*
+ * Get the list of questions containing an attribute "ArbitraryResponse".
+ * Currently we only look at single response questions.
+ */
+const getArbitraryQuestions = (Children) => {
+ const arbitraryQuestions = [];
+ const childr = Children.filter((children) => children.Child?.length !== 0);
+ childr.forEach((item) => {
+ item.Child?.forEach((component) => {
+ // component is a subsequence
+ if (component.type === 'SequenceType') {
+ component.Child.forEach((question) => {
+ if (
+ // single choice question
+ question.questionType === SINGLE_CHOICE &&
+ question.ArbitraryResponse !== undefined
+ ) {
+ arbitraryQuestions.push(question);
+ }
+ });
+ } else if (
+ // component is a single choice question
+ component.questionType === SINGLE_CHOICE &&
+ component.ArbitraryResponse !== undefined
+ ) {
+ arbitraryQuestions.push(component);
+ }
+ });
+ });
+ return arbitraryQuestions;
+};
+
+/*
+ * Get the list of arbitrary variables.
+ * An arbitrary variable is the "ArbitraryResponse" object of a question,
+ * extented with "arbitraryVariableOfVariableId" which is the corresponding Response variable id.
+ */
+export const getArbitraryVariablesFromRemote = (Children) => {
+ const arbitraryVariables = [];
+ const arbitraryQuestions = getArbitraryQuestions(Children);
+
+ arbitraryQuestions.forEach((question) => {
+ // a question with arbitrary is a singleReponse question so it has only one Response
+ const responseVariableId = question.Response[0].CollectedVariableReference;
+
+ const arbitraryVariable = {
+ ...question.ArbitraryResponse,
+ arbitraryVariableOfVariableId: responseVariableId,
+ };
+ arbitraryVariables.push(arbitraryVariable);
+ });
+
+ return arbitraryVariables;
+};
+
function remoteToVariableResponseNested(children = [], acc = {}) {
children.forEach((child) => {
const {
@@ -226,6 +281,7 @@ function remoteToState(remote, componentGroup, codesListsStore) {
Control: controls,
Response: responses,
ClarificationQuestion: responsesClarification,
+ ArbitraryResponse: arbitraryResponse,
ResponseStructure: responseStructure,
Child: children,
parent,
@@ -250,6 +306,10 @@ function remoteToState(remote, componentGroup, codesListsStore) {
responseFinal = responseFinal.concat(clar.Response);
});
}
+ if (arbitraryResponse !== undefined) {
+ responseFinal.push(arbitraryResponse);
+ }
+
const state = {
id,
name,
@@ -629,6 +689,37 @@ function getClarificationResponseTableQuestion(
};
}
+function getArbitraryResponse(collectedVariablesStore, collectedVariables) {
+ const collectedVariableQuestions = [];
+ Object.values(collectedVariablesStore).forEach((collec) => {
+ if (collectedVariables !== undefined) {
+ collectedVariables.forEach((variables) => {
+ if (collec.id === variables) {
+ collectedVariableQuestions.push(collec);
+ }
+ });
+ }
+ });
+
+ for (const collected of collectedVariableQuestions) {
+ const isArbitraryVariable =
+ collected.arbitraryVariableOfVariableId !== undefined;
+ // we can have only one collected variable that is an arbitrary variable
+ if (isArbitraryVariable) {
+ const responseModel = {
+ mandatory: false,
+ typeName: collected.type,
+ maxLength: collected.TEXT.maxLength,
+ collectedVariable: collected.id,
+ };
+ const arbitraryResponse = Response.stateToRemote(responseModel);
+ return arbitraryResponse;
+ }
+ }
+
+ return undefined;
+}
+
function storeToRemoteNested(
state,
store,
@@ -686,8 +777,15 @@ function storeToRemoteNested(
responsesClarification,
flowControl,
);
+
remote.FlowControl = remoteclarification.flowcontrolefinal;
remote.ClarificationQuestion = remoteclarification.ClarificationQuestion;
+
+ const remoteArbitrary = getArbitraryResponse(
+ collectedVariablesStore,
+ collectedVariables,
+ );
+ remote.ArbitraryResponse = remoteArbitrary;
}
if (
responseFormat.type === MULTIPLE_CHOICE &&
diff --git a/src/model/transformations/component.spec.jsx b/src/model/transformations/component.spec.jsx
index e8c4c121d..54bca51e9 100644
--- a/src/model/transformations/component.spec.jsx
+++ b/src/model/transformations/component.spec.jsx
@@ -38,6 +38,43 @@ describe('component tranformations', () => {
questionType: 'SIMPLE',
type: 'QuestionType',
},
+ {
+ Control: [],
+ Declaration: [],
+ FlowControl: [],
+ Label: ['question'],
+ Name: 'QUESTION',
+ Response: [
+ {
+ CollectedVariableReference: 'm6awmxim',
+ CodeListReference: 'listId',
+ Datatype: {
+ typeName: 'TEXT',
+ type: 'TextDatatypeType',
+ MaxLength: 1,
+ allowArbitraryResponse: true,
+ visualizationHint: 'SUGGESTER',
+ },
+ id: 'm5qyf09t',
+ mandatory: false,
+ },
+ ],
+ ArbitraryResponse: {
+ id: 'm6awrflv',
+ Datatype: {
+ type: 'TextDatatypeType',
+ typeName: 'TEXT',
+ MaxLength: 249,
+ },
+ mandatory: false,
+ CollectedVariableReference: 'm6awei7a',
+ },
+ TargetMode: ['CAWI'],
+ depth: 2,
+ id: 'm5qy1yf9',
+ questionType: 'SINGLE_CHOICE',
+ type: 'QuestionType',
+ },
],
Control: [],
Declaration: [],
@@ -64,7 +101,7 @@ describe('component tranformations', () => {
type: 'SequenceType',
},
],
- CodeLists: { CodeList: [] },
+ CodeLists: { CodeList: [{ id: 'listId', Label: 'listLabel' }] },
ComponentGroup: [
{
Label: ['Components for page 1'],
@@ -98,6 +135,30 @@ describe('component tranformations', () => {
id: 'khj4uvok',
type: 'CollectedVariableType',
},
+ {
+ id: 'm6awmxim',
+ Name: 'SUGGESTER',
+ type: 'CollectedVariableType',
+ Label: 'SUGGESTER label',
+ Datatype: {
+ type: 'TextDatatypeType',
+ Pattern: '',
+ typeName: 'TEXT',
+ MaxLength: 1,
+ },
+ CodeListReference: 'L_PRODEAP_EAU_2024',
+ },
+ {
+ id: 'm6awei7a',
+ Name: 'SUGGESTER_ARBITRARY',
+ type: 'CollectedVariableType',
+ Label: 'SUGGESTER_ARBITRARY label',
+ Datatype: {
+ type: 'TextDatatypeType',
+ typeName: 'TEXT',
+ MaxLength: 249,
+ },
+ },
],
},
agency: 'fr.insee',
@@ -109,7 +170,7 @@ describe('component tranformations', () => {
owner: 'FAKEPERMISSION',
};
const questionnaireId = 'khj4mf0r';
- const codesListsStore = {};
+ const codesListsStore = { id: { label: 'label' } };
const iterations = [];
const filters = [];
@@ -156,6 +217,35 @@ describe('component tranformations', () => {
type: 'QUESTION',
weight: 0,
},
+ m5qy1yf9: {
+ TargetMode: ['CAWI'],
+ children: [],
+ collectedVariables: ['m6awmxim', 'm6awei7a'],
+ controls: {},
+ declarations: {},
+ flowControl: [],
+ dynamiqueSpecified: 'Redirections',
+ id: 'm5qy1yf9',
+ label: 'question',
+ name: 'QUESTION',
+ parent: 'khj54mjm',
+ redirections: {},
+ responseFormat: {
+ type: 'SINGLE_CHOICE',
+ SINGLE_CHOICE: {
+ visHint: 'SUGGESTER',
+ allowArbitraryResponse: true,
+ id: 'm5qyf09t',
+ mandatory: false,
+ CodesList: {
+ id: 'listId',
+ },
+ },
+ },
+ responsesClarification: undefined,
+ type: 'QUESTION',
+ weight: 1,
+ },
khj4mf0r: {
TargetMode: ['CAWI'],
children: ['khj54mjm'],
@@ -174,7 +264,7 @@ describe('component tranformations', () => {
},
khj54mjm: {
TargetMode: ['CAWI'],
- children: ['khj4le7i'],
+ children: ['khj4le7i', 'm5qy1yf9'],
controls: {},
declarations: {},
flowControl: [],
diff --git a/src/model/transformations/response-format-single.jsx b/src/model/transformations/response-format-single.jsx
index 7321d4eb8..fbce6748b 100644
--- a/src/model/transformations/response-format-single.jsx
+++ b/src/model/transformations/response-format-single.jsx
@@ -11,7 +11,7 @@ export function remoteToState(remote) {
const {
responses: [
{
- Datatype: { visualizationHint: visHint },
+ Datatype: { allowArbitraryResponse, visualizationHint: visHint },
mandatory,
CodeListReference,
id,
@@ -24,6 +24,7 @@ export function remoteToState(remote) {
CodeList.remoteToState(CodeListReference),
id,
mandatory,
+ allowArbitraryResponse,
visHint,
};
}
@@ -31,6 +32,7 @@ export function remoteToState(remote) {
export function stateToRemote(state, collectedVariables) {
const {
[DEFAULT_CODES_LIST_SELECTOR_PATH]: { id: codesListId },
+ allowArbitraryResponse,
visHint,
mandatory,
id,
@@ -40,6 +42,7 @@ export function stateToRemote(state, collectedVariables) {
Response.stateToRemote({
id,
mandatory,
+ allowArbitraryResponse,
visHint,
codesListId,
typeName: TEXT,
diff --git a/src/model/transformations/response.jsx b/src/model/transformations/response.jsx
index 934c35c24..a4edfa07a 100644
--- a/src/model/transformations/response.jsx
+++ b/src/model/transformations/response.jsx
@@ -1,4 +1,7 @@
-import { DATATYPE_TYPE_FROM_NAME } from '../../constants/pogues-constants';
+import {
+ DATATYPE_TYPE_FROM_NAME,
+ DATATYPE_VIS_HINT,
+} from '../../constants/pogues-constants';
import { uuid } from '../../utils/utils';
export function stateToRemote(state, response) {
@@ -23,6 +26,7 @@ export function stateToRemote(state, response) {
mayears: Mayears,
mamonths: Mamonths,
codesListId: CodeListReference,
+ allowArbitraryResponse,
visHint: visualizationHint,
collectedVariable: CollectedVariableReference,
} = state;
@@ -47,6 +51,12 @@ export function stateToRemote(state, response) {
model.CodeListReference = CodeListReference;
if (mandatory !== undefined)
model.mandatory = mandatory === '' ? false : mandatory;
+ if (allowArbitraryResponse !== undefined)
+ // we keep allowArbitraryResponse value only for suggester
+ model.Datatype.allowArbitraryResponse =
+ visualizationHint === DATATYPE_VIS_HINT.SUGGESTER
+ ? allowArbitraryResponse
+ : undefined;
if (visualizationHint !== undefined)
model.Datatype.visualizationHint = visualizationHint;
if (MaxLength !== undefined) model.Datatype.MaxLength = MaxLength;
diff --git a/src/model/transformations/response.spec.jsx b/src/model/transformations/response.spec.jsx
index cca8afab2..1e2d7b7ed 100644
--- a/src/model/transformations/response.spec.jsx
+++ b/src/model/transformations/response.spec.jsx
@@ -1,7 +1,10 @@
import { format } from 'url';
import { describe, expect, test } from 'vitest';
-import { DATATYPE_TYPE_FROM_NAME } from '../../constants/pogues-constants';
+import {
+ DATATYPE_TYPE_FROM_NAME,
+ DATATYPE_VIS_HINT,
+} from '../../constants/pogues-constants';
import { stateToRemote } from './response';
describe('response tranformations', () => {
@@ -166,6 +169,39 @@ describe('response tranformations', () => {
expect(result.Datatype.Unit).toEqual(unit);
});
+ test('when allowArbitraryResponse is defined', () => {
+ const typeName = 'TEXT';
+ const allowArbitraryResponse = true;
+
+ // Get all values of DATATYPE_VIS_HINT except 'SUGGESTER'
+ const nonSuggesterVisHints = Object.values(DATATYPE_VIS_HINT).filter(
+ (visHint) => visHint !== DATATYPE_VIS_HINT.SUGGESTER,
+ );
+
+ const resultSuggester = stateToRemote({
+ typeName,
+ id: '1',
+ visHint: DATATYPE_VIS_HINT.SUGGESTER,
+ allowArbitraryResponse,
+ });
+
+ expect(resultSuggester.Datatype.allowArbitraryResponse).toEqual(
+ allowArbitraryResponse,
+ );
+
+ // Test for all non-SUGGESTER visHint values
+ nonSuggesterVisHints.forEach((visHint) => {
+ const result = stateToRemote({
+ typeName,
+ id: '1',
+ visHint,
+ allowArbitraryResponse,
+ });
+
+ expect(result.Datatype.allowArbitraryResponse).toEqual(undefined);
+ });
+ });
+
test('when Format is defined', () => {
const typeName = 'DATE';
const result = stateToRemote({
diff --git a/src/utils/variables/collected-variables-utils.jsx b/src/utils/variables/collected-variables-utils.jsx
index 922d38aac..8d0562e7a 100644
--- a/src/utils/variables/collected-variables-utils.jsx
+++ b/src/utils/variables/collected-variables-utils.jsx
@@ -1,6 +1,7 @@
import {
COMPONENT_TYPE,
DATATYPE_NAME,
+ DATATYPE_VIS_HINT,
DEFAULT_CODES_LIST_SELECTOR_PATH,
DIMENSION_FORMATS,
DIMENSION_TYPE,
@@ -86,6 +87,7 @@ export function getCollectedVariable(
coordinates,
reponseFormatValues = {},
alternativeLabel = '',
+ arbitraryVariableOfVariableId = undefined,
) {
let collectedVariable = {
...reponseFormatValues,
@@ -97,6 +99,11 @@ export function getCollectedVariable(
if (coordinates) collectedVariable = { ...collectedVariable, ...coordinates };
if (alternativeLabel)
collectedVariable = { ...collectedVariable, ...alternativeLabel };
+ if (arbitraryVariableOfVariableId)
+ collectedVariable = {
+ ...collectedVariable,
+ arbitraryVariableOfVariableId: arbitraryVariableOfVariableId,
+ };
return collectedVariable;
}
@@ -168,8 +175,12 @@ export function getCollectedVariablesMultiple(
export function getCollectedVariablesSingle(questionName, form) {
const collectedVariables = [];
- collectedVariables.push(
- getCollectedVariable(questionName, `${questionName} label`, undefined, {
+
+ const mainCollectedVariable = getCollectedVariable(
+ questionName,
+ `${questionName} label`,
+ undefined,
+ {
codeListReference: form.CodesList.id,
codeListReferenceLabel: form.CodesList.label,
type: TEXT,
@@ -177,9 +188,35 @@ export function getCollectedVariablesSingle(questionName, form) {
maxLength: 1,
pattern: '',
},
- }),
+ },
);
+ collectedVariables.push(mainCollectedVariable);
+
+ // get arbitrary variable for suggester
+ if (
+ form.allowArbitraryResponse &&
+ form.visHint === DATATYPE_VIS_HINT.SUGGESTER
+ ) {
+ const arbitraryResponseName = `${questionName}_ARBITRARY`;
+ collectedVariables.push(
+ getCollectedVariable(
+ arbitraryResponseName,
+ `${arbitraryResponseName} label`,
+ undefined,
+ {
+ type: TEXT,
+ [TEXT]: {
+ maxLength: 249,
+ },
+ },
+ undefined,
+ mainCollectedVariable.id,
+ ),
+ );
+ }
+
+ // get clarification variables for codes lists
form.CodesList.codes?.forEach((code) => {
if (code.precisionid && code.precisionid !== '') {
collectedVariables.push(
diff --git a/src/utils/variables/collected-variables-utils.spec.jsx b/src/utils/variables/collected-variables-utils.spec.jsx
index 8bcae0407..e15460374 100644
--- a/src/utils/variables/collected-variables-utils.spec.jsx
+++ b/src/utils/variables/collected-variables-utils.spec.jsx
@@ -39,7 +39,7 @@ describe('generateCollectedVariables', () => {
});
describe('getCollectedVariablesSingle', () => {
- test('should return collected variables for QCM without precision in codesList', () => {
+ test('should return collected variables for QCU without precision in codesList', () => {
const questionName = 'questionName';
const form = {
CodesList: {
@@ -89,7 +89,7 @@ describe('getCollectedVariablesSingle', () => {
]);
});
- test('should return collected variables for QCM with precision in codesList', () => {
+ test('should return collected variables for QCU with precision in codesList', () => {
const questionName = 'questionName';
const form = {
CodesList: {
@@ -150,6 +150,37 @@ describe('getCollectedVariablesSingle', () => {
},
]);
});
+
+ test('should return suggester arbitrary variable if allowArbitraryResponse', () => {
+ const questionName = 'questionName';
+ const form = {
+ allowArbitraryResponse: true,
+ visHint: 'SUGGESTER',
+ CodesList: { id: 'id', label: 'label', codes: [] },
+ };
+
+ const result = getCollectedVariablesSingle(questionName, form);
+
+ expect(result).toEqual([
+ {
+ id: result[0].id,
+ name: 'questionName',
+ label: 'questionName label',
+ codeListReference: form.CodesList.id,
+ codeListReferenceLabel: form.CodesList.label,
+ type: 'TEXT',
+ TEXT: { maxLength: 1, pattern: '' },
+ },
+ {
+ id: result[1].id,
+ name: 'questionName_ARBITRARY',
+ label: 'questionName_ARBITRARY label',
+ type: 'TEXT',
+ TEXT: { maxLength: 249 },
+ arbitraryVariableOfVariableId: result[0].id,
+ },
+ ]);
+ });
});
describe('getCollectedVariablesTable', () => {
diff --git a/src/widgets/component-new-edit/components/response-format/single/response-format-single.jsx b/src/widgets/component-new-edit/components/response-format/single/response-format-single.jsx
index ff46711de..bc7a272ce 100644
--- a/src/widgets/component-new-edit/components/response-format/single/response-format-single.jsx
+++ b/src/widgets/component-new-edit/components/response-format/single/response-format-single.jsx
@@ -37,6 +37,7 @@ function ResponseFormatSingle({
path,
formName,
allowPrecision,
+ disableSetArbitrary,
}) {
const selectorPath = responseFormatType;
@@ -168,7 +169,24 @@ function ResponseFormatSingle({
)}
{visHint === SUGGESTER ? (
-
+ <>
+
+ {!disableSetArbitrary && (
+ value === 'true'}
+ // Convert true/false/undefined to string "true"/"false" when displaying the form
+ format={(value) => (value === true ? 'true' : 'false')}
+ >
+ {Dictionary.yes}
+ {Dictionary.no}
+
+ )}
+ >
) : (
{
@@ -212,6 +232,7 @@ const mapStateToProps = (state, { selectorPathParent, responseFormatType }) => {
componentsStore: state.appState.activeComponentsById,
collectedVariablesStore: state.appState.collectedVariableByQuestion,
visHint: selector(state, `${path}visHint`),
+ allowArbitraryResponse: selector(state, `${path}allowArbitraryResponse`),
path,
};
};
diff --git a/src/widgets/component-new-edit/components/response-format/table/input-measure.jsx b/src/widgets/component-new-edit/components/response-format/table/input-measure.jsx
index 54a835595..dd23b2d8b 100644
--- a/src/widgets/component-new-edit/components/response-format/table/input-measure.jsx
+++ b/src/widgets/component-new-edit/components/response-format/table/input-measure.jsx
@@ -44,6 +44,7 @@ function InputMeasure({ selectorPath }) {
selectorPathParent={selectorPath}
showMandatory={false}
allowPrecision={false}
+ disableSetArbitrary={true}
/>