From 9532e223deae35bb5b25ed49370f0199c699e1fc Mon Sep 17 00:00:00 2001
From: Quentin Ruhier <99256289+QRuhier@users.noreply.github.com>
Date: Thu, 7 Nov 2024 10:04:05 +0100
Subject: [PATCH] feat: add VTL formula for primary table dimension (#857)
* feat: add VTL formula for primary table dimension
* fix: do not save isFixedLength for non_dynamic table
* test: fix tests
* test: add new tests
* refactor : remove unused table props 'totalLabel' & 'showTotalLabel'
see b7e4a97da93e265e9a4a39fd88f77fb6d3b8458a
* refactor : remove unused table props 'totalLabel' & 'showTotalLabel'
see b7e4a97da93e265e9a4a39fd88f77fb6d3b8458a
* refactor : update form structure
* fix: uppercase in model variables + rename min/max
* test : update current tests & add new tests
* fix: wrong field label for linesNbCalculation
* chore: remove commented code
---
src/actions/metadata.jsx | 7 +-
src/constants/dictionary.jsx | 24 +-
src/constants/pogues-constants.jsx | 6 +
.../response-format-table.jsx | 90 ++--
.../collected-variable.spec.jsx | 11 +-
src/model/transformations/dimension.jsx | 26 +-
src/model/transformations/dimension.spec.jsx | 140 +++--
.../response-format-multiple.spec.jsx | 36 +-
.../transformations/response-format-table.jsx | 60 ++-
.../response-format-table.spec.jsx | 488 +++++++++++++++++-
src/utils/validation/validation-rules.jsx | 18 +-
.../collected-variables-utils.spec.jsx | 18 +-
.../table/table-primary-list-dynamic.jsx | 28 +
.../table/table-primary-list-fixed.jsx | 21 +
.../table/table-primary-list.jsx | 56 +-
15 files changed, 789 insertions(+), 240 deletions(-)
create mode 100644 src/widgets/component-new-edit/components/response-format/table/table-primary-list-dynamic.jsx
create mode 100644 src/widgets/component-new-edit/components/response-format/table/table-primary-list-fixed.jsx
diff --git a/src/actions/metadata.jsx b/src/actions/metadata.jsx
index 60710fbde..94691f354 100644
--- a/src/actions/metadata.jsx
+++ b/src/actions/metadata.jsx
@@ -1,3 +1,4 @@
+import { DIMENSION_LENGTH } from '../constants/pogues-constants';
import {
getUnitsList,
getSeries,
@@ -9,6 +10,7 @@ import {
getNomenclature,
} from '../utils/remote-api';
+const { NON_DYNAMIC } = DIMENSION_LENGTH;
export const LOAD_METADATA_SUCCESS = 'LOAD_METADATA_SUCCESS';
export const LOAD_METADATA_FAILURE = 'LOAD_METADATA_FAILURE';
const LOAD_SERIES = 'LOAD_SERIES';
@@ -212,7 +214,10 @@ const isQuestionLoop = component => {
component.type === 'QuestionType' &&
((component.questionType === 'TABLE' &&
component.ResponseStructure.Dimension.some(
- dim => dim.dimensionType === 'PRIMARY' && dim.dynamic !== '0',
+ dim =>
+ dim.dimensionType === 'PRIMARY' &&
+ dim.dynamic !== '0' &&
+ dim.dynamic !== NON_DYNAMIC,
)) ||
component.questionType === 'PAIRING')
);
diff --git a/src/constants/dictionary.jsx b/src/constants/dictionary.jsx
index 6f1230a34..a7eb05559 100644
--- a/src/constants/dictionary.jsx
+++ b/src/constants/dictionary.jsx
@@ -875,6 +875,14 @@ const dictionary = {
fr: 'Liste',
en: 'List',
},
+ minMax: {
+ fr: 'Min/Max',
+ en: 'Min/max',
+ },
+ linesNbCalculation: {
+ fr: 'Calcul du nombre de lignes',
+ en: 'Calculation of number of lines',
+ },
minRowNb: {
fr: 'Nombre de lignes min.',
en: 'Number of lines min.',
@@ -883,22 +891,6 @@ const dictionary = {
fr: 'Nombre de lignes max.',
en: 'Number of lines max.',
},
- rowTotal: {
- fr: 'Ajouter la ligne "Total"',
- en: 'Add the "Total" row',
- },
- rowTotalLabel: {
- fr: 'Libellé de la ligne "Total"',
- en: 'Label of the "Total" row',
- },
- columnTotal: {
- fr: 'Ajouter la colonne "Total"',
- en: 'Add the "Total" column',
- },
- columnTotalLabel: {
- fr: 'Libellé de la colonne "Total"',
- en: 'Label of the "Total" column',
- },
addScndAxis: {
fr: 'Ajouter un axe secondaire',
en: 'Add a secondary axis',
diff --git a/src/constants/pogues-constants.jsx b/src/constants/pogues-constants.jsx
index 7d5bfd889..e62870b7b 100644
--- a/src/constants/pogues-constants.jsx
+++ b/src/constants/pogues-constants.jsx
@@ -78,6 +78,12 @@ export const DIMENSION_FORMATS = {
BOOL: 'BOOL',
};
+export const DIMENSION_LENGTH = {
+ NON_DYNAMIC: 'NON_DYNAMIC',
+ DYNAMIC_LENGTH: 'DYNAMIC_LENGTH',
+ FIXED_LENGTH: 'FIXED_LENGTH',
+};
+
export const QUESTION_TYPE_ENUM = {
SIMPLE: 'SIMPLE',
SINGLE_CHOICE: 'SINGLE_CHOICE',
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 bd1a81162..7dbd01d73 100644
--- a/src/model/formToState/component-new-edit/response-format-table.jsx
+++ b/src/model/formToState/component-new-edit/response-format-table.jsx
@@ -2,12 +2,13 @@ import cloneDeep from 'lodash.clonedeep';
import merge from 'lodash.merge';
import { verifyVariable } from '../../../utils/utils';
-import { Factory as CodesListFactory } from '../..';
+import { Factory as CodesListFactory } from '../codes-lists/codes-list';
import {
DATATYPE_NAME,
DATATYPE_VIS_HINT,
DEFAULT_CODES_LIST_SELECTOR_PATH,
DIMENSION_FORMATS,
+ DIMENSION_LENGTH,
DIMENSION_TYPE,
QUESTION_TYPE_ENUM,
UI_BEHAVIOUR,
@@ -15,6 +16,7 @@ import {
const { PRIMARY, SECONDARY, MEASURE, LIST_MEASURE } = DIMENSION_TYPE;
const { LIST, CODES_LIST } = DIMENSION_FORMATS;
+const { DYNAMIC_LENGTH, FIXED_LENGTH } = DIMENSION_LENGTH;
const { SIMPLE, SINGLE_CHOICE } = QUESTION_TYPE_ENUM;
const { DATE, NUMERIC, TEXT, BOOLEAN, DURATION } = DATATYPE_NAME;
const { RADIO } = DATATYPE_VIS_HINT;
@@ -80,15 +82,21 @@ export const defaultMeasureForm = {
},
};
+const defaultPrimaryListState = {
+ type: DYNAMIC_LENGTH,
+ [DYNAMIC_LENGTH]: {
+ minLines: 0,
+ maxLines: 0,
+ },
+ [FIXED_LENGTH]: {
+ fixedLength: '',
+ },
+};
+
export const defaultState = {
[PRIMARY]: {
- showTotalLabel: '0',
- totalLabel: '',
type: LIST,
- [LIST]: {
- numLinesMin: 0,
- numLinesMax: 0,
- },
+ [LIST]: defaultPrimaryListState,
[CODES_LIST]: {
// [DEFAULT_CODES_LIST_SELECTOR_PATH]: cloneDeep(CodesListDefaultState),
},
@@ -96,8 +104,6 @@ export const defaultState = {
[SECONDARY]: {
// [DEFAULT_CODES_LIST_SELECTOR_PATH]: cloneDeep(CodesListDefaultState),
showSecondaryAxis: false,
- showTotalLabel: '0',
- totalLabel: '',
},
[LIST_MEASURE]: {
...defaultMeasureState,
@@ -107,17 +113,23 @@ export const defaultState = {
};
export function formToStatePrimary(form, codesListPrimary) {
- const { showTotalLabel, totalLabel, type, [type]: primaryForm } = form;
+ const { type, [type]: primaryForm } = form;
const state = {
- showTotalLabel,
- totalLabel,
type,
};
if (type === LIST) {
- const { numLinesMin, numLinesMax } = primaryForm;
- state[LIST] = { numLinesMin, numLinesMax };
+ const {
+ type: listType,
+ [listType]: { minLines, maxLines, fixedLength },
+ } = primaryForm;
+
+ state[LIST] = {
+ type: listType,
+ [listType]:
+ listType === DYNAMIC_LENGTH ? { minLines, maxLines } : { fixedLength },
+ };
} else {
const { [DEFAULT_CODES_LIST_SELECTOR_PATH]: codesListForm } = primaryForm;
state[CODES_LIST] = {
@@ -132,14 +144,10 @@ export function formToStatePrimary(form, codesListPrimary) {
export function formToStateSecondary(form, codesListSecondary) {
const {
showSecondaryAxis,
- showTotalLabel,
- totalLabel,
[DEFAULT_CODES_LIST_SELECTOR_PATH]: codesListForm,
} = form;
return {
showSecondaryAxis,
- showTotalLabel,
- totalLabel,
[DEFAULT_CODES_LIST_SELECTOR_PATH]:
codesListSecondary.formToStateComponent(codesListForm),
};
@@ -210,11 +218,9 @@ export function formToState(form, transformers) {
}
export function stateToFormPrimary(currentState, codesListPrimary) {
- const { showTotalLabel, totalLabel, type, [LIST]: listState } = currentState;
+ const { type, [LIST]: listState } = currentState;
return {
- showTotalLabel,
- totalLabel,
type,
[LIST]: { ...listState },
[CODES_LIST]: {
@@ -225,11 +231,9 @@ export function stateToFormPrimary(currentState, codesListPrimary) {
}
export function stateToFormSecondary(currentState, codesListSecondary) {
- const { showSecondaryAxis, showTotalLabel, totalLabel } = currentState;
+ const { showSecondaryAxis } = currentState;
return {
showSecondaryAxis,
- showTotalLabel,
- totalLabel,
[DEFAULT_CODES_LIST_SELECTOR_PATH]:
codesListSecondary.stateComponentToForm(),
};
@@ -426,34 +430,28 @@ const Factory = (initialState = {}, codesListsStore) => {
getNormalizedValues: form => {
// Values ready to be validated
const {
- [PRIMARY]: {
- type: typePrimary,
- [typePrimary]: primary,
- showTotalLabel: showTotalLabelPrimary,
- totalLabel: totalLabelPrimary,
- },
- [SECONDARY]: {
- showSecondaryAxis,
- showTotalLabel: showTotalLabelSecondary,
- totalLabel: totalLabelSecondary,
- ...others
- },
+ [PRIMARY]: { type: typePrimary, [typePrimary]: primary },
+ [SECONDARY]: { showSecondaryAxis, ...others },
[MEASURE]: measure,
[LIST_MEASURE]: { measures: listMeasures, ...listMeasuresInput },
} = form;
// Normalized primary axis values
- const normalized = {
- [PRIMARY]: {
+ const normalized = {};
+
+ if (typePrimary === CODES_LIST) {
+ normalized[PRIMARY] = {
type: typePrimary,
- showTotalLabel: showTotalLabelPrimary,
[typePrimary]: primary,
- },
- };
-
- if (showTotalLabelPrimary === '1') {
- normalized[PRIMARY].totalLabel = totalLabelPrimary;
+ };
+ }
+ if (typePrimary === LIST) {
+ const { type: listType, [listType]: listContent } = primary;
+ normalized[PRIMARY] = {
+ type: typePrimary,
+ [typePrimary]: { type: listType, [listType]: listContent },
+ };
}
if (typePrimary === CODES_LIST && showSecondaryAxis) {
@@ -462,11 +460,7 @@ const Factory = (initialState = {}, codesListsStore) => {
normalized[SECONDARY] = {
...others,
showSecondaryAxis,
- showTotalLabelSecondary,
};
- if (showTotalLabelSecondary === '1') {
- normalized[SECONDARY].totalLabel = totalLabelSecondary;
- }
// Normalized measure axis values
diff --git a/src/model/transformations/collected-variable.spec.jsx b/src/model/transformations/collected-variable.spec.jsx
index 4ea5ddc87..6333fecc4 100644
--- a/src/model/transformations/collected-variable.spec.jsx
+++ b/src/model/transformations/collected-variable.spec.jsx
@@ -853,7 +853,10 @@ describe('collected variable tranformations', () => {
},
],
PRIMARY: {
- LIST: { numLinesMin: 4, numLinesMax: 2 },
+ LIST: {
+ DYNAMIC_LENGTH: { minLines: 2, maxLines: 4 },
+ type: 'DYNAMIC_LENGTH',
+ },
type: 'LIST',
},
},
@@ -983,7 +986,11 @@ describe('collected variable tranformations', () => {
},
],
PRIMARY: {
- LIST: { numLinesMin: 4, numLinesMax: 2 },
+ LIST: {
+ DYNAMIC_LENGTH: { minLines: 0, maxLines: 0 },
+ FIXED_LENGTH: { fixedLength: '' },
+ type: 'DYNAMIC_LENGTH',
+ },
type: 'LIST',
},
},
diff --git a/src/model/transformations/dimension.jsx b/src/model/transformations/dimension.jsx
index 1ca4697e1..b03f3b894 100644
--- a/src/model/transformations/dimension.jsx
+++ b/src/model/transformations/dimension.jsx
@@ -1,18 +1,19 @@
import {
DIMENSION_TYPE,
+ DIMENSION_LENGTH,
DEFAULT_CODES_LIST_SELECTOR_PATH,
} from '../../constants/pogues-constants';
const { PRIMARY, SECONDARY, MEASURE } = DIMENSION_TYPE;
+const { DYNAMIC_LENGTH, FIXED_LENGTH, NON_DYNAMIC } = DIMENSION_LENGTH;
export function stateToRemote(state) {
const {
type,
[DEFAULT_CODES_LIST_SELECTOR_PATH]: CodesListState,
- numLinesMin,
- numLinesMax,
- showTotalLabel,
- totalLabel,
+ fixedLength,
+ minLines,
+ maxLines,
label: Label,
} = state;
const model = {
@@ -21,9 +22,19 @@ export function stateToRemote(state) {
if (type === PRIMARY || type === SECONDARY) {
if (CodesListState) model.CodeListReference = CodesListState.id;
- if (showTotalLabel && totalLabel) model.totalLabel = totalLabel;
- if (numLinesMin !== undefined && numLinesMax !== undefined)
- model.dynamic = `${numLinesMin}-${numLinesMax}`;
+ }
+
+ if (type === PRIMARY) {
+ if (fixedLength !== undefined) {
+ model.dynamic = FIXED_LENGTH;
+ model.FixedLength = fixedLength;
+ } else if (minLines !== undefined && maxLines !== undefined) {
+ model.MinLines = minLines;
+ model.MaxLines = maxLines;
+ model.dynamic = DYNAMIC_LENGTH;
+ } else {
+ model.dynamic = NON_DYNAMIC;
+ }
}
if (type === MEASURE && Label) {
@@ -32,7 +43,6 @@ export function stateToRemote(state) {
return {
dimensionType: '',
- dynamic: '0',
...model,
};
}
diff --git a/src/model/transformations/dimension.spec.jsx b/src/model/transformations/dimension.spec.jsx
index d5db1e7dc..ed31b8b97 100644
--- a/src/model/transformations/dimension.spec.jsx
+++ b/src/model/transformations/dimension.spec.jsx
@@ -13,7 +13,6 @@ describe('dimension tranformations', () => {
});
expect(result).toEqual({
- dynamic: '0',
dimensionType: MEASURE,
});
});
@@ -24,94 +23,91 @@ describe('dimension tranformations', () => {
});
expect(result).toEqual({
- dynamic: '0',
dimensionType: MEASURE,
Label: 'Label',
});
});
- [PRIMARY, SECONDARY].forEach(type => {
- test(`when the type is ${type} and has a CodesListState`, () => {
- const result = stateToRemote({
- type: type,
- [DEFAULT_CODES_LIST_SELECTOR_PATH]: { id: '1' },
- });
-
- expect(result).toEqual({
- dynamic: '0',
- dimensionType: type,
- CodeListReference: '1',
- });
- });
- test(`when the type is ${type} and has a totalLabel and showTotalLabel=true`, () => {
- const result = stateToRemote({
- type: type,
- totalLabel: 'totalLabel',
- showTotalLabel: true,
- });
+ test(`when the type is PRIMARY and has a CodesListState`, () => {
+ const result = stateToRemote({
+ type: PRIMARY,
+ [DEFAULT_CODES_LIST_SELECTOR_PATH]: { id: '1' },
+ });
- expect(result).toEqual({
- dynamic: '0',
- dimensionType: type,
- totalLabel: 'totalLabel',
- });
- });
- test(`when the type is ${type} and has a totalLabel and showTotalLabel=false`, () => {
- const result = stateToRemote({
- type: type,
- totalLabel: 'totalLabel',
- showTotalLabel: false,
- });
+ expect(result).toEqual({
+ dynamic: 'NON_DYNAMIC',
+ dimensionType: PRIMARY,
+ CodeListReference: '1',
+ });
+ });
+ test(`when the type is PRIMARY and has a minLines and maxLines`, () => {
+ const result = stateToRemote({
+ type: PRIMARY,
+ minLines: 1,
+ maxLines: 2,
+ });
- expect(result).toEqual({
- dynamic: '0',
- dimensionType: type,
- });
- });
- test(`when the type is ${type} and has a numLinesMin and numLinesMax`, () => {
- const result = stateToRemote({
- type: type,
- numLinesMin: 1,
- numLinesMax: 2,
- });
+ expect(result).toEqual({
+ dimensionType: PRIMARY,
+ dynamic: 'DYNAMIC_LENGTH',
+ MinLines: 1,
+ MaxLines: 2,
+ });
+ });
+ test(`when the type is PRIMARY and has a length fixed by a formula`, () => {
+ const result = stateToRemote({
+ type: PRIMARY,
+ fixedLength: 'formula',
+ });
- expect(result).toEqual({
- dimensionType: type,
- dynamic: '1-2',
- });
- });
- test(`when the type is ${type} and has a numLinesMin but not numLinesMax`, () => {
- const result = stateToRemote({
- type: type,
- numLinesMin: 1,
- });
+ expect(result).toEqual({
+ dimensionType: PRIMARY,
+ dynamic: 'FIXED_LENGTH',
+ FixedLength: 'formula',
+ });
+ });
+ test(`when the type is PRIMARY and has a minLines but not maxLines`, () => {
+ const result = stateToRemote({
+ type: PRIMARY,
+ minLines: 1,
+ });
- expect(result).toEqual({
- dynamic: '0',
- dimensionType: type,
- });
- });
- test(`when the type is ${type} and has a numLinesMax but not numLinesMin`, () => {
- const result = stateToRemote({
- type: type,
- numLinesMax: 2,
- });
+ expect(result).toEqual({
+ dynamic: 'NON_DYNAMIC',
+ dimensionType: PRIMARY,
+ });
+ });
+ test(`when the type is PRIMARY and has a maxLines but not minLines`, () => {
+ const result = stateToRemote({
+ type: PRIMARY,
+ maxLines: 2,
+ });
- expect(result).toEqual({
- dynamic: '0',
- dimensionType: type,
- });
+ expect(result).toEqual({
+ dynamic: 'NON_DYNAMIC',
+ dimensionType: PRIMARY,
});
});
- test('when the type is not PRIMARY, SECONDATY neither MEASURE', () => {
+ test(`when the type is SECONDARY and has a CodesListState`, () => {
const result = stateToRemote({
- type: 'FAKE TYPE',
+ type: SECONDARY,
+ [DEFAULT_CODES_LIST_SELECTOR_PATH]: { id: '1' },
});
expect(result).toEqual({
- dynamic: '0',
- dimensionType: 'FAKE TYPE',
+ dimensionType: SECONDARY,
+ CodeListReference: '1',
});
});
});
+
+test('when the type is not PRIMARY, SECONDATY neither MEASURE', () => {
+ const result = stateToRemote({
+ type: 'FAKE TYPE',
+ });
+
+ expect(result).toEqual({
+ dimensionType: 'FAKE TYPE',
+ });
+});
diff --git a/src/model/transformations/response-format-multiple.spec.jsx b/src/model/transformations/response-format-multiple.spec.jsx
index dd2dae004..a8e2a8258 100644
--- a/src/model/transformations/response-format-multiple.spec.jsx
+++ b/src/model/transformations/response-format-multiple.spec.jsx
@@ -2,7 +2,7 @@ import { describe, expect, it } from 'vitest';
import { remoteToState, stateToRemote } from './response-format-multiple';
describe('response format multiple', () => {
- it('remoteToState', () => {
+ it('remoteToState, with old modelization of dynamic', () => {
const remote = {
responses: [
{
@@ -28,6 +28,32 @@ describe('response format multiple', () => {
expect(output).toEqual(expected);
});
+ it('remoteToState, with new modelizaation of dynamic', () => {
+ const remote = {
+ responses: [
+ {
+ id: 'jf0wxgwc',
+ Datatype: { typeName: 'BOOLEAN', type: 'BooleanDatatypeType' },
+ CollectedVariableReference: 'jf0wtl3p',
+ },
+ ],
+ dimensions: [
+ {
+ dimensionType: 'PRIMARY',
+ dynamic: 'NON_DYNAMIC',
+ CodeListReference: 'jf0w3fab',
+ },
+ { dimensionType: 'MEASURE' },
+ ],
+ };
+ const output = remoteToState(remote);
+ const expected = {
+ MEASURE: { BOOL: {}, type: 'BOOL' },
+ PRIMARY: { CodesList: { id: 'jf0w3fab' } },
+ };
+ expect(output).toEqual(expected);
+ });
+
describe('stateToRemote', () => {
it('should return the state representation of a table if question is new', () => {
const state = {
@@ -66,9 +92,9 @@ describe('response format multiple', () => {
{
CodeListReference: 'jf0w3fab',
dimensionType: 'PRIMARY',
- dynamic: '0',
+ dynamic: 'NON_DYNAMIC',
},
- { dimensionType: 'MEASURE', dynamic: '0' },
+ { dimensionType: 'MEASURE' },
],
Mapping: [{ MappingSource: output.Response[0].id, MappingTarget: '1' }],
Response: [
@@ -150,9 +176,9 @@ describe('response format multiple', () => {
{
CodeListReference: 'kgs19ihv',
dimensionType: 'PRIMARY',
- dynamic: '0',
+ dynamic: 'NON_DYNAMIC',
},
- { dimensionType: 'MEASURE', dynamic: '0' },
+ { dimensionType: 'MEASURE' },
],
Mapping: [
{ MappingSource: 'kgs1hrro', MappingTarget: '1' },
diff --git a/src/model/transformations/response-format-table.jsx b/src/model/transformations/response-format-table.jsx
index 5872cd3b7..23e22e6c5 100644
--- a/src/model/transformations/response-format-table.jsx
+++ b/src/model/transformations/response-format-table.jsx
@@ -14,10 +14,12 @@ import {
QUESTION_TYPE_ENUM,
DATATYPE_NAME,
DEFAULT_CODES_LIST_SELECTOR_PATH,
+ DIMENSION_LENGTH,
} from '../../constants/pogues-constants';
const { PRIMARY, SECONDARY, MEASURE, LIST_MEASURE } = DIMENSION_TYPE;
const { LIST, CODES_LIST } = DIMENSION_FORMATS;
+const { DYNAMIC_LENGTH, FIXED_LENGTH } = DIMENSION_LENGTH;
const { SIMPLE, SINGLE_CHOICE } = QUESTION_TYPE_ENUM;
const { TEXT } = DATATYPE_NAME;
@@ -119,22 +121,27 @@ function getMeasuresModel(responses, dimensions, offset) {
}
function parseDynamic(dynamic) {
- return dynamic.split('-').map(v => {
- return v.length > 0 ? parseInt(v, 10) : 0;
- });
+ // if it still uses the old format 'min-max'
+ if (dynamic.includes('-')) {
+ const minMax = dynamic.split('-').map(v => parseInt(v, 10));
+
+ // Check if we have exactly two valid numbers
+ if (minMax.length === 2 && !isNaN(minMax[0]) && !isNaN(minMax[1])) {
+ return minMax;
+ }
+ }
+
+ // Default case: return [0, 0] for '0', 'NON_DYNAMIC', or any invalid format
+ return [0, 0];
}
// REMOTE TO STATE
function remoteToStatePrimary(remote) {
- const { totalLabel, dynamic, CodeListReference } = remote;
+ const { dynamic, CodeListReference, FixedLength, MinLines, MaxLines } =
+ remote;
let state = {};
- if (totalLabel) {
- state.showTotalLabel = '1';
- state.totalLabel = totalLabel;
- }
-
if (CodeListReference) {
state = {
...state,
@@ -145,13 +152,23 @@ function remoteToStatePrimary(remote) {
},
};
} else {
- const [numLinesMin, numLinesMax] = parseDynamic(dynamic);
+ const [minLines, maxLines] =
+ dynamic === 'DYNAMIC_LENGTH'
+ ? [MinLines, MaxLines]
+ : parseDynamic(dynamic);
+
state = {
...state,
type: LIST,
[LIST]: {
- numLinesMin: numLinesMin,
- numLinesMax: numLinesMax,
+ type: dynamic === 'FIXED_LENGTH' ? FIXED_LENGTH : DYNAMIC_LENGTH,
+ [FIXED_LENGTH]: {
+ fixedLength: FixedLength,
+ },
+ [DYNAMIC_LENGTH]: {
+ minLines,
+ maxLines,
+ },
},
};
}
@@ -160,18 +177,13 @@ function remoteToStatePrimary(remote) {
}
function remoteToStateSecondary(remote) {
- const { totalLabel, CodeListReference } = remote;
+ const { CodeListReference } = remote;
const state = {
showSecondaryAxis: true,
[DEFAULT_CODES_LIST_SELECTOR_PATH]:
CodeList.remoteToState(CodeListReference),
};
- if (totalLabel) {
- state.showTotalLabel = '1';
- state.totalLabel = totalLabel;
- }
-
return state;
}
@@ -339,17 +351,23 @@ export function stateToRemote(
const {
type,
[type]: { type: typePrimaryCodesList, ...primaryTypeState },
- ...totalLabelPrimaryState
} = primaryState;
const dimensionsModel = [];
let responsesState = [];
+ let primaryListTypeState = {};
+ if (type === LIST) {
+ const listTypeState = primaryTypeState[typePrimaryCodesList];
+ if (listTypeState) {
+ primaryListTypeState = { ...listTypeState };
+ }
+ }
+
// Primary and secondary dimension
dimensionsModel.push(
Dimension.stateToRemote({
type: PRIMARY,
- ...primaryTypeState,
- ...totalLabelPrimaryState,
+ ...(type === LIST ? primaryListTypeState : primaryTypeState),
}),
);
if (secondaryState) {
diff --git a/src/model/transformations/response-format-table.spec.jsx b/src/model/transformations/response-format-table.spec.jsx
index c9723e110..837ca7807 100644
--- a/src/model/transformations/response-format-table.spec.jsx
+++ b/src/model/transformations/response-format-table.spec.jsx
@@ -2,7 +2,7 @@ import { describe, expect, it } from 'vitest';
import { remoteToState, stateToRemote } from './response-format-table';
describe('remoteToState', () => {
- it('should use an offset equal to 1', () => {
+ it('should use an offset equal to 1, with old modelization of dimension', () => {
const remote = {
responses: [
{
@@ -93,8 +93,14 @@ describe('remoteToState', () => {
PRIMARY: {
type: 'LIST',
LIST: {
- numLinesMin: 1,
- numLinesMax: 10,
+ type: 'DYNAMIC_LENGTH',
+ DYNAMIC_LENGTH: {
+ minLines: 1,
+ maxLines: 10,
+ },
+ FIXED_LENGTH: {
+ fixedLength: undefined,
+ },
},
},
LIST_MEASURE: [
@@ -156,7 +162,165 @@ describe('remoteToState', () => {
};
expect(remoteToState(remote, codesListsStore)).toEqual(output);
});
- it('with hierarchical codes', () => {
+ it('should use an offset equal to 1, with new modelization of dimension', () => {
+ const remote = {
+ responses: [
+ {
+ id: 'joy1vnzz',
+ Datatype: {
+ typeName: 'TEXT',
+ type: 'TextDatatypeType',
+ MaxLength: 249,
+ Pattern: '',
+ },
+ CollectedVariableReference: 'joxzq5qe',
+ },
+ {
+ id: 'joy1ujjc',
+ Datatype: {
+ typeName: 'NUMERIC',
+ type: 'NumericDatatypeType',
+ Minimum: '0',
+ Maximum: '10',
+ Decimals: '',
+ },
+ CollectedVariableReference: 'joxzsdwi',
+ },
+
+ {
+ id: 'k1ai8yzv',
+ Datatype: {
+ typeName: 'DATE',
+ type: 'DateDatatypeType',
+ Format: 'dd-mm-yyyy',
+ Minimum: '',
+ Maximum: '',
+ },
+ CollectedVariableReference: 'k1ail7ly',
+ },
+
+ {
+ id: 'k1tcqelo',
+ Datatype: {
+ Format: 'PTnHnM',
+ Mahours: '2',
+ Maminutes: '1',
+ Mamonths: '',
+ Maximum: 'PT2H1M',
+ Mayears: '',
+ Mihours: '1',
+ Miminutes: '1',
+ Mimonths: '',
+ Minimum: 'PT1H1M',
+ Miyears: '',
+ type: 'DurationDatatypeType',
+ typeName: 'DURATION',
+ },
+ CollectedVariableReference: 'k1tcqec4',
+ },
+ ],
+ dimensions: [
+ {
+ dimensionType: 'PRIMARY',
+ dynamic: 'DYNAMIC_LENGTH',
+ MinLines: 1,
+ MaxLines: 10,
+ },
+ {
+ dimensionType: 'MEASURE',
+ Label: 'mes1',
+ },
+ {
+ dimensionType: 'MEASURE',
+ Label: 'mes2',
+ },
+ {
+ dimensionType: 'MEASURE',
+ Label: 'mes3',
+ },
+ {
+ dimensionType: 'MEASURE',
+ Label: 'mes4',
+ },
+ ],
+ };
+
+ const codesListsStore = {};
+
+ const output = {
+ PRIMARY: {
+ type: 'LIST',
+ LIST: {
+ type: 'DYNAMIC_LENGTH',
+ DYNAMIC_LENGTH: {
+ minLines: 1,
+ maxLines: 10,
+ },
+ FIXED_LENGTH: {
+ fixedLength: undefined,
+ },
+ },
+ },
+ LIST_MEASURE: [
+ {
+ label: 'mes1',
+ type: 'SIMPLE',
+ SIMPLE: {
+ type: 'TEXT',
+ TEXT: {
+ maxLength: 249,
+ pattern: '',
+ },
+ },
+ },
+
+ {
+ label: 'mes2',
+ type: 'SIMPLE',
+ SIMPLE: {
+ type: 'NUMERIC',
+ NUMERIC: {
+ minimum: '0',
+ maximum: '10',
+ decimals: '',
+ },
+ },
+ },
+
+ {
+ label: 'mes3',
+ type: 'SIMPLE',
+ SIMPLE: {
+ type: 'DATE',
+ DATE: {
+ minimum: '',
+ maximum: '',
+ format: 'dd-mm-yyyy',
+ },
+ },
+ },
+
+ {
+ label: 'mes4',
+ type: 'SIMPLE',
+ SIMPLE: {
+ type: 'DURATION',
+ DURATION: {
+ maximum: 'PT2H1M',
+ minimum: 'PT1H1M',
+ format: 'PTnHnM',
+ mihours: '1',
+ miminutes: '1',
+ mahours: '2',
+ maminutes: '1',
+ },
+ },
+ },
+ ],
+ };
+ expect(remoteToState(remote, codesListsStore)).toEqual(output);
+ });
+ it('with hierarchical codes, with old modelization of dimension', () => {
const remote = {
responses: [
{
@@ -258,7 +422,107 @@ describe('remoteToState', () => {
};
expect(remoteToState(remote, codesListsStore)).toEqual(output);
});
- it('without secondary axes', () => {
+ it('with hierarchical codes, with new modelization of dimension', () => {
+ const remote = {
+ responses: [
+ {
+ id: 'jjjyttfv',
+ Datatype: {
+ typeName: 'TEXT',
+ type: 'TextDatatypeType',
+ MaxLength: 249,
+ Pattern: '',
+ },
+ CollectedVariableReference: 'jjjyjq15',
+ },
+
+ {
+ id: 'jjjz2i63',
+ Datatype: {
+ typeName: 'TEXT',
+ type: 'TextDatatypeType',
+ MaxLength: 249,
+ Pattern: '',
+ },
+ CollectedVariableReference: 'jjjyymbc',
+ },
+ ],
+ dimensions: [
+ {
+ dimensionType: 'PRIMARY',
+ dynamic: 'NON_DYNAMIC',
+ CodeListReference: 'jjjyt2ta',
+ },
+ {
+ dimensionType: 'MEASURE',
+ Label: 'measure1',
+ },
+ {
+ dimensionType: 'MEASURE',
+ Label: 'measure2',
+ },
+ ],
+ };
+ const codesListsStore = {
+ jjjyt2ta: {
+ id: 'jjjyt2ta',
+ label: 'new',
+ codes: {
+ a1: {
+ value: 'a1',
+ label: 'a1',
+ parent: '',
+ depth: 1,
+ weight: 1,
+ },
+ a2: {
+ value: 'a2',
+ label: 'a2',
+ parent: 'a1',
+ depth: 2,
+ weight: 1,
+ },
+ },
+ name: '',
+ },
+ };
+ const output = {
+ PRIMARY: {
+ type: 'CODES_LIST',
+ CODES_LIST: {
+ CodesList: {
+ id: 'jjjyt2ta',
+ },
+ },
+ },
+ LIST_MEASURE: [
+ {
+ label: 'measure1',
+ type: 'SIMPLE',
+ SIMPLE: {
+ type: 'TEXT',
+ TEXT: {
+ maxLength: 249,
+ pattern: '',
+ },
+ },
+ },
+ {
+ label: 'measure2',
+ type: 'SIMPLE',
+ SIMPLE: {
+ type: 'TEXT',
+ TEXT: {
+ maxLength: 249,
+ pattern: '',
+ },
+ },
+ },
+ ],
+ };
+ expect(remoteToState(remote, codesListsStore)).toEqual(output);
+ });
+ it('without secondary axes, with old modelization of dimension', () => {
const remote = {
responses: [
{
@@ -311,11 +575,97 @@ describe('remoteToState', () => {
type: 'SIMPLE',
},
],
- PRIMARY: { LIST: { numLinesMax: 3, numLinesMin: 1 }, type: 'LIST' },
+ PRIMARY: {
+ LIST: {
+ type: 'DYNAMIC_LENGTH',
+ DYNAMIC_LENGTH: {
+ minLines: 1,
+ maxLines: 3,
+ },
+ FIXED_LENGTH: {
+ fixedLength: undefined,
+ },
+ },
+ type: 'LIST',
+ },
};
expect(remoteToState(remote, codesListsStore)).toEqual(output);
});
- it('with secondary axes', () => {
+ it('without secondary axes, with new modelization of dimension', () => {
+ const remote = {
+ responses: [
+ {
+ id: 'jf0vzwbp',
+ Datatype: {
+ typeName: 'TEXT',
+ type: 'TextDatatypeType',
+ MaxLength: 249,
+ Pattern: '',
+ },
+ CollectedVariableReference: 'jf0vzlbq',
+ },
+ {
+ id: 'jf0vunia',
+ Datatype: {
+ typeName: 'TEXT',
+ type: 'TextDatatypeType',
+ MaxLength: 249,
+ Pattern: '',
+ },
+ CollectedVariableReference: 'jf0vjphy',
+ },
+ {
+ id: 'jf0vqmpo',
+ Datatype: {
+ typeName: 'TEXT',
+ type: 'TextDatatypeType',
+ MaxLength: 249,
+ Pattern: '',
+ },
+ CollectedVariableReference: 'jf0vyha5',
+ },
+ ],
+ dimensions: [
+ {
+ dimensionType: 'PRIMARY',
+ dynamic: 'DYNAMIC_LENGTH',
+ MinLines: 1,
+ MaxLines: 3,
+ },
+ { dimensionType: 'MEASURE', Label: 'mneasure1' },
+ ],
+ };
+ const codesListsStore = {};
+ const output = {
+ LIST_MEASURE: [
+ {
+ SIMPLE: {
+ TEXT: { maxLength: 249, pattern: '' },
+ id: undefined,
+ mandatory: undefined,
+ type: 'TEXT',
+ },
+ label: 'mneasure1',
+ type: 'SIMPLE',
+ },
+ ],
+ PRIMARY: {
+ LIST: {
+ type: 'DYNAMIC_LENGTH',
+ DYNAMIC_LENGTH: {
+ minLines: 1,
+ maxLines: 3,
+ },
+ FIXED_LENGTH: {
+ fixedLength: undefined,
+ },
+ },
+ type: 'LIST',
+ },
+ };
+ expect(remoteToState(remote, codesListsStore)).toEqual(output);
+ });
+ it('with secondary axes, with old modelization of dimension', () => {
const remote = {
responses: [
{
@@ -392,16 +742,96 @@ describe('remoteToState', () => {
};
expect(remoteToState(remote, codesListsStore)).toEqual(output);
});
+ it('with secondary axes, with new modelization of dimension', () => {
+ const remote = {
+ responses: [
+ {
+ id: 'jf0vblxi',
+ Datatype: {
+ typeName: 'TEXT',
+ type: 'TextDatatypeType',
+ MaxLength: 249,
+ Pattern: '',
+ },
+ CollectedVariableReference: 'jf0vahmg',
+ },
+ ],
+ dimensions: [
+ {
+ dimensionType: 'PRIMARY',
+ dynamic: 'NON_DYNAMIC',
+ CodeListReference: 'jf0vbzj9',
+ },
+ {
+ dimensionType: 'SECONDARY',
+ CodeListReference: 'jf0vj3il',
+ },
+ { dimensionType: 'MEASURE', Label: 'fsdfsdfsdf' },
+ ],
+ };
+ const codesListsStore = {
+ jf0vbzj9: {
+ id: 'jf0vbzj9',
+ label: 'code list',
+ codes: {
+ c1: { value: 'c1', label: 'asd', parent: '', depth: 1, weight: 1 },
+ },
+ name: '',
+ },
+ jf0vj3il: {
+ id: 'jf0vj3il',
+ label: 'code lisg 23',
+ codes: {
+ df: { value: 'df', label: 'sdfs', parent: '', depth: 1, weight: 1 },
+ },
+ name: '',
+ },
+ };
+ const output = {
+ MEASURE: {
+ SIMPLE: {
+ TEXT: {
+ maxLength: 249,
+ pattern: '',
+ },
+ id: undefined,
+ mandatory: undefined,
+ type: 'TEXT',
+ },
+ label: 'fsdfsdfsdf',
+ type: 'SIMPLE',
+ },
+ PRIMARY: {
+ CODES_LIST: {
+ CodesList: {
+ id: 'jf0vbzj9',
+ },
+ },
+ type: 'CODES_LIST',
+ },
+ SECONDARY: {
+ CodesList: {
+ id: 'jf0vj3il',
+ },
+ showSecondaryAxis: true,
+ },
+ };
+ expect(remoteToState(remote, codesListsStore)).toEqual(output);
+ });
});
describe('stateToRemote', () => {
it('without secondary axes', () => {
const state = {
PRIMARY: {
- showTotalLabel: '0',
- totalLabel: '',
type: 'LIST',
- LIST: { numLinesMin: '2', numLinesMax: '3' },
+ LIST: {
+ type: 'DYNAMIC_LENGTH',
+ DYNAMIC_LENGTH: {
+ minLines: 2,
+ maxLines: 3,
+ },
+ },
},
LIST_MEASURE: [
{
@@ -455,12 +885,13 @@ describe('stateToRemote', () => {
expect(result.Dimension).toEqual([
{
dimensionType: 'PRIMARY',
- dynamic: '2-3',
+ dynamic: 'DYNAMIC_LENGTH',
+ MinLines: 2,
+ MaxLines: 3,
},
{
Label: 'measure 1',
dimensionType: 'MEASURE',
- dynamic: '0',
},
]);
@@ -498,15 +929,11 @@ describe('stateToRemote', () => {
it('with secondary axes', () => {
const state = {
PRIMARY: {
- showTotalLabel: '0',
- totalLabel: '',
type: 'CODES_LIST',
CODES_LIST: { CodesList: { id: 'jf0vbzj9' } },
},
SECONDARY: {
showSecondaryAxis: true,
- showTotalLabel: '0',
- totalLabel: '',
CodesList: { id: 'jf0vj3il' },
},
MEASURE: {
@@ -541,13 +968,16 @@ describe('stateToRemote', () => {
);
expect(result.Dimension).toEqual([
- { CodeListReference: 'jf0vbzj9', dimensionType: 'PRIMARY', dynamic: '0' },
+ {
+ CodeListReference: 'jf0vbzj9',
+ dimensionType: 'PRIMARY',
+ dynamic: 'NON_DYNAMIC',
+ },
{
CodeListReference: 'jf0vj3il',
dimensionType: 'SECONDARY',
- dynamic: '0',
},
- { Label: 'fsdfsdfsdf', dimensionType: 'MEASURE', dynamic: '0' },
+ { Label: 'fsdfsdfsdf', dimensionType: 'MEASURE' },
]);
expect(result.Attribute).toEqual([
@@ -570,7 +1000,7 @@ describe('stateToRemote', () => {
expect(outputMapping[0].MappingTarget).toEqual('1 1');
});
- it('get responses id when edting question', () => {
+ it('get responses id when editing question', () => {
const state = {
LIST_MEASURE: [
{
@@ -583,9 +1013,13 @@ describe('stateToRemote', () => {
},
],
PRIMARY: {
- LIST: { numLinesMin: 2, numLinesMax: 1 },
- showTotalLabel: '0',
- totalLabel: '',
+ LIST: {
+ type: 'DYNAMIC_LENGTH',
+ DYNAMIC_LENGTH: {
+ minLines: 1,
+ maxLines: 2,
+ },
+ },
type: 'LIST',
},
};
@@ -630,11 +1064,15 @@ describe('stateToRemote', () => {
);
expect(result.Dimension).toEqual([
- { dimensionType: 'PRIMARY', dynamic: '2-1' },
+ {
+ dimensionType: 'PRIMARY',
+ dynamic: 'DYNAMIC_LENGTH',
+ MinLines: 1,
+ MaxLines: 2,
+ },
{
Label: 'testlibe',
dimensionType: 'MEASURE',
- dynamic: '0',
},
]);
diff --git a/src/utils/validation/validation-rules.jsx b/src/utils/validation/validation-rules.jsx
index 3c134e8e8..6e09be612 100644
--- a/src/utils/validation/validation-rules.jsx
+++ b/src/utils/validation/validation-rules.jsx
@@ -28,12 +28,14 @@ import {
DIMENSION_FORMATS,
DATATYPE_NAME,
DEFAULT_CODES_LIST_SELECTOR_PATH,
+ DIMENSION_LENGTH,
} from '../../constants/pogues-constants';
import Dictionary from '../dictionary/dictionary';
const { SIMPLE, SINGLE_CHOICE, MULTIPLE_CHOICE, TABLE } = QUESTION_TYPE_ENUM;
const { NUMERIC, TEXT, DATE, DURATION } = DATATYPE_NAME;
const { PRIMARY, SECONDARY, LIST_MEASURE, MEASURE } = DIMENSION_TYPE;
+const { DYNAMIC_LENGTH, FIXED_LENGTH } = DIMENSION_LENGTH;
const { LIST, CODES_LIST } = DIMENSION_FORMATS;
const {
RESPONSE_FORMAT,
@@ -121,18 +123,14 @@ export const questionRules = {
[validCodesList],
[`${RESPONSE_FORMAT}.${MULTIPLE_CHOICE}.${MEASURE}.${CODES_LIST}.${DEFAULT_CODES_LIST_SELECTOR_PATH}`]:
[validCodesList],
- [`${RESPONSE_FORMAT}.${TABLE}.${PRIMARY}.totalLabel`]: [required],
- [`${RESPONSE_FORMAT}.${TABLE}.${PRIMARY}.${LIST}.numLinesMin`]: [
- value => minValue(1)(value),
- value => maxValue(300)(value),
- ],
- [`${RESPONSE_FORMAT}.${TABLE}.${PRIMARY}.${LIST}.numLinesMax`]: [
- value => minValue(1)(value),
- value => maxValue(300)(value),
- ],
+ [`${RESPONSE_FORMAT}.${TABLE}.${PRIMARY}.${LIST}.${DYNAMIC_LENGTH}.minLines`]:
+ [value => minValue(1)(value), value => maxValue(300)(value)],
+ [`${RESPONSE_FORMAT}.${TABLE}.${PRIMARY}.${LIST}.${DYNAMIC_LENGTH}.maxLines`]:
+ [value => minValue(1)(value), value => maxValue(300)(value)],
+ [`${RESPONSE_FORMAT}.${TABLE}.${PRIMARY}.${LIST}.${FIXED_LENGTH}.fixedLength`]:
+ [required],
[`${RESPONSE_FORMAT}.${TABLE}.${PRIMARY}.${CODES_LIST}.${DEFAULT_CODES_LIST_SELECTOR_PATH}`]:
[validCodesList],
- [`${RESPONSE_FORMAT}.${TABLE}.${SECONDARY}.totalLabel`]: [required],
[`${RESPONSE_FORMAT}.${TABLE}.${SECONDARY}.${DEFAULT_CODES_LIST_SELECTOR_PATH}`]:
[validCodesList],
[`${RESPONSE_FORMAT}.${TABLE}.label`]: [required],
diff --git a/src/utils/variables/collected-variables-utils.spec.jsx b/src/utils/variables/collected-variables-utils.spec.jsx
index 283a41efa..40d5e2d65 100644
--- a/src/utils/variables/collected-variables-utils.spec.jsx
+++ b/src/utils/variables/collected-variables-utils.spec.jsx
@@ -158,12 +158,11 @@ describe('getCollectedVariablesTable', () => {
const questionName = 'QUESTION';
const form = {
PRIMARY: {
- showTotalLabel: '0',
- totalLabel: '',
type: 'CODES_LIST',
LIST: {
- numLinesMin: '0',
- numLinesMax: 0,
+ DYNAMIC_LENGTH: { minLines: 0, maxLines: 0 },
+ FIXED_LENGTH: { fixedLength: '' },
+ type: 'DYNAMIC_LENGTH',
},
CODES_LIST: {
CodesList: {
@@ -189,8 +188,6 @@ describe('getCollectedVariablesTable', () => {
codes: [],
},
showSecondaryAxis: false,
- showTotalLabel: '0',
- totalLabel: '',
},
LIST_MEASURE: {
label: '',
@@ -332,12 +329,11 @@ describe('getCollectedVariablesTable', () => {
const questionName = 'QUESTION';
const form = {
PRIMARY: {
- showTotalLabel: '0',
- totalLabel: '',
type: 'LIST',
LIST: {
- numLinesMin: '1',
- numLinesMax: '4',
+ DYNAMIC_LENGTH: { minLines: 1, maxLines: 3 },
+ FIXED_LENGTH: { fixedLength: '' },
+ type: 'DYNAMIC_LENGTH',
},
CODES_LIST: {
CodesList: {
@@ -354,8 +350,6 @@ describe('getCollectedVariablesTable', () => {
codes: [],
},
showSecondaryAxis: false,
- showTotalLabel: '0',
- totalLabel: '',
},
LIST_MEASURE: {
label: '',
diff --git a/src/widgets/component-new-edit/components/response-format/table/table-primary-list-dynamic.jsx b/src/widgets/component-new-edit/components/response-format/table/table-primary-list-dynamic.jsx
new file mode 100644
index 000000000..15065b78b
--- /dev/null
+++ b/src/widgets/component-new-edit/components/response-format/table/table-primary-list-dynamic.jsx
@@ -0,0 +1,28 @@
+import React from 'react';
+import { FormSection, Field } from 'redux-form';
+import Dictionary from '../../../../../utils/dictionary/dictionary';
+import Input from '../../../../../forms/controls/input';
+import { DIMENSION_LENGTH } from '../../../../../constants/pogues-constants';
+
+const { DYNAMIC_LENGTH: selectorPath } = DIMENSION_LENGTH;
+
+export function ResponseFormatTablePrincipalListDynamic() {
+ return (
+