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 ( + + + + + ); +} diff --git a/src/widgets/component-new-edit/components/response-format/table/table-primary-list-fixed.jsx b/src/widgets/component-new-edit/components/response-format/table/table-primary-list-fixed.jsx new file mode 100644 index 000000000..15205f65e --- /dev/null +++ b/src/widgets/component-new-edit/components/response-format/table/table-primary-list-fixed.jsx @@ -0,0 +1,21 @@ +import React from 'react'; +import { FormSection, Field } from 'redux-form'; +import Dictionary from '../../../../../utils/dictionary/dictionary'; +import { InputWithVariableAutoCompletion } from '../../../../../forms/controls/control-with-suggestions'; +import { DIMENSION_LENGTH } from '../../../../../constants/pogues-constants'; + +const { FIXED_LENGTH: selectorPath } = DIMENSION_LENGTH; + +export function ResponseFormatTablePrincipalListFixed() { + return ( + + + + ); +} diff --git a/src/widgets/component-new-edit/components/response-format/table/table-primary-list.jsx b/src/widgets/component-new-edit/components/response-format/table/table-primary-list.jsx index 6d04f9a45..e548b9fa5 100644 --- a/src/widgets/component-new-edit/components/response-format/table/table-primary-list.jsx +++ b/src/widgets/component-new-edit/components/response-format/table/table-primary-list.jsx @@ -1,33 +1,49 @@ import React from 'react'; -import { FormSection, Field } from 'redux-form'; +import { FormSection } from 'redux-form'; import Dictionary from '../../../../../utils/dictionary/dictionary'; -import Input from '../../../../../forms/controls/input'; -import { DIMENSION_FORMATS } from '../../../../../constants/pogues-constants'; +import { + DIMENSION_FORMATS, + DIMENSION_LENGTH, +} from '../../../../../constants/pogues-constants'; +import { SelectorView, View } from '../../../../selector-view'; +import { ResponseFormatTablePrincipalListDynamic } from './table-primary-list-dynamic'; +import { ResponseFormatTablePrincipalListFixed } from './table-primary-list-fixed'; const { LIST: selectorPath } = DIMENSION_FORMATS; +const { DYNAMIC_LENGTH, FIXED_LENGTH } = DIMENSION_LENGTH; + +export default function ResponseFormatTablePrincipalList({ + selectorPathParent, +}) { + const selectorPathComposed = selectorPathParent + ? `${selectorPathParent}.${selectorPath}` + : selectorPath; -function ResponseFormatTablePrincipalList() { return (
- - + + + + + + + +
); } - -export default ResponseFormatTablePrincipalList;