diff --git a/src/constants/dictionary.ts b/src/constants/dictionary.ts
index 6501fef52..5621db533 100644
--- a/src/constants/dictionary.ts
+++ b/src/constants/dictionary.ts
@@ -889,6 +889,10 @@ const dictionary: Dictionary = {
fr: 'Retrouver dans le questionnaire',
en: 'Retrieve in the questionnaire',
},
+ allowArbitraryResponse: {
+ fr: 'Autoriser une réponse libre',
+ en: 'Allow arbitrary response',
+ },
list: {
fr: 'Liste',
en: 'List',
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 0e3441a23..ce1c00227 100644
--- a/src/model/formToState/component-new-edit/response-format-single.jsx
+++ b/src/model/formToState/component-new-edit/response-format-single.jsx
@@ -11,6 +11,7 @@ import {
const { RADIO } = DATATYPE_VIS_HINT;
export const defaultState = {
+ allowArbitrary: false,
mandatory: false,
hasSpecialCode: false,
specialLabel: '',
@@ -22,6 +23,7 @@ export const defaultState = {
};
export const defaultForm = {
+ allowArbitrary: false,
mandatory: false,
hasSpecialCode: false,
specialLabel: '',
@@ -35,6 +37,7 @@ export const defaultForm = {
export function formToState(form, transformers) {
const {
id,
+ allowArbitrary,
mandatory,
visHint,
hasSpecialCode,
@@ -47,6 +50,7 @@ export function formToState(form, transformers) {
return {
id,
+ allowArbitrary,
mandatory,
visHint,
hasSpecialCode,
@@ -64,6 +68,7 @@ export function formToState(form, transformers) {
export function stateToForm(currentState, transformers) {
const {
id,
+ allowArbitrary,
visHint,
mandatory,
hasSpecialCode,
@@ -75,6 +80,7 @@ export function stateToForm(currentState, transformers) {
return {
id,
+ allowArbitrary,
mandatory,
visHint,
hasSpecialCode,
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 a557417e2..86c5619c0 100644
--- a/src/model/formToState/component-new-edit/response-format-table.jsx
+++ b/src/model/formToState/component-new-edit/response-format-table.jsx
@@ -67,6 +67,7 @@ export const defaultMeasureState = {
cloneDeep(CodesListDefaultState),
{ id: uuid() },
),
+ allowArbitrary: false,
visHint: RADIO,
},
};
@@ -76,6 +77,7 @@ export const defaultMeasureForm = {
type: SIMPLE,
[SIMPLE]: defaultMeasureSimpleState,
[SINGLE_CHOICE]: {
+ allowArbitrary: false,
hasSpecialCode: false,
specialLabel: '',
specialCode: '',
@@ -172,13 +174,17 @@ export function formToStateMeasure(form, codesListMeasure) {
[simpleType]: { ...simpleForm },
};
} else {
- const { visHint, [DEFAULT_CODES_LIST_SELECTOR_PATH]: codesListForm } =
- measureForm;
+ const {
+ allowArbitrary,
+ visHint,
+ [DEFAULT_CODES_LIST_SELECTOR_PATH]: codesListForm,
+ } = measureForm;
const codesList = codesListMeasure
? codesListMeasure.formToStateComponent(codesListForm)
: CodesListFactory().formToState(codesListForm);
state[SINGLE_CHOICE] = {
+ allowArbitrary,
visHint,
[DEFAULT_CODES_LIST_SELECTOR_PATH]: codesList,
};
@@ -253,6 +259,7 @@ export function stateToFormMeasure(
type,
[SIMPLE]: simpleState,
[SINGLE_CHOICE]: {
+ allowArbitrary,
visHint,
[DEFAULT_CODES_LIST_SELECTOR_PATH]: codesListState,
},
@@ -273,6 +280,7 @@ export function stateToFormMeasure(
type,
[SIMPLE]: simpleState,
[SINGLE_CHOICE]: {
+ allowArbitrary,
visHint,
[DEFAULT_CODES_LIST_SELECTOR_PATH]: codesListForm,
},
@@ -282,6 +290,7 @@ export function stateToFormMeasure(
export function stateToFormMeasureList(currentState, codesListsStore) {
const {
[SINGLE_CHOICE]: {
+ allowArbitrary,
visHint,
[DEFAULT_CODES_LIST_SELECTOR_PATH]: codesListState,
},
@@ -295,6 +304,7 @@ export function stateToFormMeasureList(currentState, codesListsStore) {
return {
...currentState,
[SINGLE_CHOICE]: {
+ allowArbitrary,
visHint,
[DEFAULT_CODES_LIST_SELECTOR_PATH]: codesListForm,
},
@@ -377,6 +387,7 @@ const Factory = (initialState = {}, codesListsStore) => {
state[SINGLE_CHOICE] = {
[DEFAULT_CODES_LIST_SELECTOR_PATH]:
codesListsStore[measureState[DEFAULT_CODES_LIST_SELECTOR_PATH].id],
+ allowArbitrary: measureState.allowArbitrary,
visHint: measureState.visHint,
};
} else {
diff --git a/src/model/transformations/response-format-single.jsx b/src/model/transformations/response-format-single.jsx
index f0c0d3a4e..65c65c1da 100644
--- a/src/model/transformations/response-format-single.jsx
+++ b/src/model/transformations/response-format-single.jsx
@@ -12,7 +12,7 @@ export function remoteToState(remote) {
const {
responses: [
{
- Datatype: { visualizationHint: visHint },
+ Datatype: { allowArbitrary, visualizationHint: visHint },
mandatory,
nonResponseModality,
CodeListReference,
@@ -26,6 +26,7 @@ export function remoteToState(remote) {
CodeList.remoteToState(CodeListReference),
id,
mandatory,
+ allowArbitrary,
visHint,
hasSpecialCode: !!nonResponseModality,
specialLabel:
@@ -45,6 +46,7 @@ export function remoteToState(remote) {
export function stateToRemote(state, collectedVariables) {
const {
[DEFAULT_CODES_LIST_SELECTOR_PATH]: { id: codesListId },
+ allowArbitrary,
visHint,
mandatory,
id,
@@ -54,6 +56,7 @@ export function stateToRemote(state, collectedVariables) {
Response.stateToRemote({
id,
mandatory,
+ allowArbitrary,
visHint,
codesListId,
typeName: TEXT,
diff --git a/src/model/transformations/response-format-table.jsx b/src/model/transformations/response-format-table.jsx
index 9c7e559b7..fb353d16b 100644
--- a/src/model/transformations/response-format-table.jsx
+++ b/src/model/transformations/response-format-table.jsx
@@ -318,6 +318,7 @@ function stateToResponseState(state) {
} else {
const {
mandatory,
+ allowArbitrary,
visHint,
[DEFAULT_CODES_LIST_SELECTOR_PATH]: { id: codesListId },
} = measureTypeState;
@@ -327,6 +328,7 @@ function stateToResponseState(state) {
typeName: TEXT,
maxLength: 1,
pattern: '',
+ allowArbitrary,
visHint,
};
}
diff --git a/src/model/transformations/response.jsx b/src/model/transformations/response.jsx
index f2d90d8d8..f8ee23667 100644
--- a/src/model/transformations/response.jsx
+++ b/src/model/transformations/response.jsx
@@ -1,5 +1,6 @@
import {
DATATYPE_TYPE_FROM_NAME,
+ DATATYPE_VIS_HINT,
UI_BEHAVIOUR,
} from '../../constants/pogues-constants';
import { uuid } from '../../utils/utils';
@@ -26,6 +27,7 @@ export function stateToRemote(state, response) {
mayears: Mayears,
mamonths: Mamonths,
codesListId: CodeListReference,
+ allowArbitrary,
visHint: visualizationHint,
hasSpecialCode,
specialLabel,
@@ -55,6 +57,12 @@ export function stateToRemote(state, response) {
model.CodeListReference = CodeListReference;
if (mandatory !== undefined)
model.mandatory = mandatory === '' ? false : mandatory;
+ if (allowArbitrary !== undefined)
+ // we keep allowArbitrary value only for suggester
+ model.Datatype.allowArbitrary =
+ visualizationHint === DATATYPE_VIS_HINT.SUGGESTER
+ ? allowArbitrary
+ : 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 4c1392959..9772bd54d 100644
--- a/src/model/transformations/response.spec.jsx
+++ b/src/model/transformations/response.spec.jsx
@@ -3,6 +3,7 @@ import { describe, expect, test } from 'vitest';
import {
DATATYPE_TYPE_FROM_NAME,
+ DATATYPE_VIS_HINT,
UI_BEHAVIOUR,
} from '../../constants/pogues-constants';
import { stateToRemote } from './response';
@@ -169,6 +170,37 @@ describe('response tranformations', () => {
expect(result.Datatype.Unit).toEqual(unit);
});
+ test('when allowArbitrary is defined', () => {
+ const typeName = 'TEXT';
+ const allowArbitrary = 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,
+ allowArbitrary,
+ });
+
+ expect(resultSuggester.Datatype.allowArbitrary).toEqual(allowArbitrary);
+
+ // Test for all non-SUGGESTER visHint values
+ nonSuggesterVisHints.forEach((visHint) => {
+ const result = stateToRemote({
+ typeName,
+ id: '1',
+ visHint,
+ allowArbitrary,
+ });
+
+ expect(result.Datatype.allowArbitrary).toEqual(undefined);
+ });
+ });
+
test('when Format is defined', () => {
const typeName = 'DATE';
const result = stateToRemote({
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..aa35a5564 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
@@ -168,7 +168,22 @@ function ResponseFormatSingle({
)}
{visHint === SUGGESTER ? (
-
+ <>
+
+ value === 'true'}
+ // Convert true/false/undefined to string "true"/"false" when displaying the form
+ format={(value) => (value === true ? 'true' : 'false')}
+ >
+ {Dictionary.yes}
+ {Dictionary.no}
+
+ >
) : (
{
componentsStore: state.appState.activeComponentsById,
collectedVariablesStore: state.appState.collectedVariableByQuestion,
visHint: selector(state, `${path}visHint`),
+ allowArbitrary: selector(state, `${path}allowArbitrary`),
path,
};
};