@arquimedes.co/eureka-forms 3.0.55 → 3.0.56-test
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/@Types/GenericFormSteps.d.ts +7 -0
- package/dist/FormSteps/CheckBoxStep/CheckBoxStep.mobileLabel.test.js +123 -0
- package/dist/FormSteps/CheckBoxStep/MaterialCheckBoxStep/MaterialCheckBoxStep.js +34 -7
- package/dist/FormSteps/CheckBoxStep/MaterialCheckBoxStep/MaterialCheckBoxStep.module.css +30 -0
- package/dist/FormSteps/PhoneInputStep/MaterialTextInputStep/MaterialPhoneInputStep.js +5 -4
- package/dist/FormSteps/StepFunctions.d.ts +16 -1
- package/dist/FormSteps/StepFunctions.js +58 -0
- package/dist/FormSteps/StepFunctions.test.js +32 -1
- package/dist/FormSteps/TextInputStep/MaterialTextInputStep/MaterialTextInputStep.js +5 -4
- package/dist/FormSteps/TitleStep/MaterialTitleStep/MaterialTitleStep.js +4 -4
- package/dist/FormSteps/Utils/@StepFiller/StepFiller.js +5 -4
- package/dist/FormSteps/Utils/MaterialInputContainer/MaterialInputContainer.js +5 -4
- package/dist/Utils/CBRFunctions.js +2 -4
- package/dist/hooks.d.ts +1 -0
- package/dist/hooks.js +1 -0
- package/package.json +1 -1
- package/dist/App/App.cbr.test.js +0 -78
- package/dist/Utils/CBRFunctions.test.d.ts +0 -1
- package/dist/Utils/CBRFunctions.test.js +0 -123
- /package/dist/{App/App.cbr.test.d.ts → FormSteps/CheckBoxStep/CheckBoxStep.mobileLabel.test.d.ts} +0 -0
|
@@ -9,6 +9,13 @@ export interface GBaseStep {
|
|
|
9
9
|
clientInfoType?: string;
|
|
10
10
|
dependencies?: string[];
|
|
11
11
|
condition?: Condition;
|
|
12
|
+
/**
|
|
13
|
+
* Per-screen column-span overrides (tablet/mobile) written by the form editor. Desktop's
|
|
14
|
+
* source of truth stays the step's legacy `size`, and a missing entry falls back to the span
|
|
15
|
+
* derived from it (see `resolveStepSpan` in FormSteps/StepFunctions), so a form with no
|
|
16
|
+
* explicit overrides renders exactly like the legacy shape.
|
|
17
|
+
*/
|
|
18
|
+
sizes?: Partial<Record<'tablet' | 'mobile', 1 | 2 | 3 | 4>>;
|
|
12
19
|
}
|
|
13
20
|
export interface GTitle extends GBaseStep {
|
|
14
21
|
title: string;
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { render, screen } from '@testing-library/react';
|
|
3
|
+
import userEvent from '@testing-library/user-event';
|
|
4
|
+
import { afterEach, describe, expect, it } from 'vitest';
|
|
5
|
+
import '@testing-library/jest-dom';
|
|
6
|
+
import { Provider } from 'react-redux';
|
|
7
|
+
import { useForm, FormProvider } from 'react-hook-form';
|
|
8
|
+
import { configureStore } from '@reduxjs/toolkit';
|
|
9
|
+
import { EurekaFormsReducer, defaultRootState } from '../../Utils/store';
|
|
10
|
+
import { RootApi } from '../../Utils/_api';
|
|
11
|
+
import FormStepTypes from '../../constants/FormStepTypes';
|
|
12
|
+
import { StoreContext } from '../../Utils/StoreContext';
|
|
13
|
+
import { IdFormContext } from '../../Contexts/FormContext';
|
|
14
|
+
import FormContext from '../../Contexts/FormContext';
|
|
15
|
+
import SectionContext from '../../Contexts/SectionContext';
|
|
16
|
+
import MaterialProviders from '../../Utils/MaterialProviders';
|
|
17
|
+
import InternalFormStyle from '../../constants/InternalFormStyle';
|
|
18
|
+
import CheckBoxStep from './CheckBoxStep';
|
|
19
|
+
import styles from './MaterialCheckBoxStep/MaterialCheckBoxStep.module.css';
|
|
20
|
+
const STORY_FORM_ID = 'STORY_FORM';
|
|
21
|
+
const LONG_LABEL = 'Cumplimiento de lo ofrecido frente a lo entregado en su vivienda';
|
|
22
|
+
const checkBoxStep = {
|
|
23
|
+
id: 'checkbox-step',
|
|
24
|
+
idSection: 'SECTION_1',
|
|
25
|
+
stepPath: ['checkbox-step'],
|
|
26
|
+
type: FormStepTypes.CHECKBOX,
|
|
27
|
+
label: LONG_LABEL,
|
|
28
|
+
description: null,
|
|
29
|
+
required: false,
|
|
30
|
+
size: 2,
|
|
31
|
+
};
|
|
32
|
+
const form = {
|
|
33
|
+
firstSection: 'SECTION_1',
|
|
34
|
+
sections: {
|
|
35
|
+
SECTION_1: { id: 'SECTION_1', name: 'Sección 1', steps: [], nextSection: null },
|
|
36
|
+
},
|
|
37
|
+
steps: { 'checkbox-step': { ...checkBoxStep } },
|
|
38
|
+
confirmationMessage: { blocks: [], entityMap: {} },
|
|
39
|
+
showLink: false,
|
|
40
|
+
size: { blockSize: 200, blockNum: 4, spacingSize: 10 },
|
|
41
|
+
};
|
|
42
|
+
function makeStore(currentBreakPoint) {
|
|
43
|
+
return configureStore({
|
|
44
|
+
reducer: { forms: EurekaFormsReducer, [RootApi.reducerPath]: RootApi.reducer },
|
|
45
|
+
preloadedState: {
|
|
46
|
+
forms: {
|
|
47
|
+
[STORY_FORM_ID]: {
|
|
48
|
+
...defaultRootState,
|
|
49
|
+
widthStats: {
|
|
50
|
+
currentBreakPoint,
|
|
51
|
+
isMobile: currentBreakPoint <= checkBoxStep.size,
|
|
52
|
+
isResponsive: true,
|
|
53
|
+
},
|
|
54
|
+
},
|
|
55
|
+
},
|
|
56
|
+
},
|
|
57
|
+
middleware: (getDefaultMiddleware) => getDefaultMiddleware().concat(RootApi.middleware),
|
|
58
|
+
});
|
|
59
|
+
}
|
|
60
|
+
function Harness({ currentBreakPoint }) {
|
|
61
|
+
const methods = useForm({ mode: 'onTouched' });
|
|
62
|
+
return (_jsx(MaterialProviders, { formStyle: InternalFormStyle, children: _jsx(Provider, { store: makeStore(currentBreakPoint), context: StoreContext, children: _jsx(IdFormContext.Provider, { value: STORY_FORM_ID, children: _jsx(FormContext.Provider, { value: form, children: _jsx(SectionContext.Provider, { value: "SECTION_1", children: _jsx(FormProvider, { ...methods, children: _jsx(CheckBoxStep, { step: checkBoxStep, editable: true }) }) }) }) }) }) }));
|
|
63
|
+
}
|
|
64
|
+
function getLabelDiv() {
|
|
65
|
+
// the leaf div holding only the label text (the wrapping column also matches by textContent)
|
|
66
|
+
return screen.getByText((_, element) => element?.tagName === 'DIV' &&
|
|
67
|
+
element.children.length === 0 &&
|
|
68
|
+
element.textContent === `${LONG_LABEL}:`);
|
|
69
|
+
}
|
|
70
|
+
/** Makes every element report a 2-line-clamped overflow (jsdom has no layout). */
|
|
71
|
+
function mockLabelOverflow() {
|
|
72
|
+
Object.defineProperty(HTMLElement.prototype, 'scrollHeight', {
|
|
73
|
+
configurable: true,
|
|
74
|
+
get() {
|
|
75
|
+
return 100;
|
|
76
|
+
},
|
|
77
|
+
});
|
|
78
|
+
Object.defineProperty(HTMLElement.prototype, 'clientHeight', {
|
|
79
|
+
configurable: true,
|
|
80
|
+
get() {
|
|
81
|
+
return 48;
|
|
82
|
+
},
|
|
83
|
+
});
|
|
84
|
+
}
|
|
85
|
+
afterEach(() => {
|
|
86
|
+
// remove the overflow mocks so other tests see jsdom defaults again
|
|
87
|
+
delete HTMLElement.prototype.scrollHeight;
|
|
88
|
+
delete HTMLElement.prototype.clientHeight;
|
|
89
|
+
});
|
|
90
|
+
describe('CheckBoxStep label on collapsed (mobile) breakpoints', () => {
|
|
91
|
+
it('wraps the label (2-line clamp) instead of single-line truncating it when the step collapses to 100% width', () => {
|
|
92
|
+
// breakpoint 1 <= size 2 → MaterialInputContainer renders the step at width: 100%
|
|
93
|
+
render(_jsx(Harness, { currentBreakPoint: 1 }));
|
|
94
|
+
const label = getLabelDiv();
|
|
95
|
+
expect(label.className).toBe(styles.clampLbl);
|
|
96
|
+
expect(label.className).not.toBe(styles.overflowLbl);
|
|
97
|
+
// the fixed desktop pixel maxWidth must not constrain the collapsed label
|
|
98
|
+
expect(label.style.maxWidth).toBe('');
|
|
99
|
+
});
|
|
100
|
+
it('keeps the single-line ellipsis label on desktop widths (control)', () => {
|
|
101
|
+
// breakpoint 4 > size 2 → the step keeps its fixed desktop width
|
|
102
|
+
render(_jsx(Harness, { currentBreakPoint: 4 }));
|
|
103
|
+
const label = getLabelDiv();
|
|
104
|
+
expect(label.className).toBe(styles.overflowLbl);
|
|
105
|
+
expect(label.style.maxWidth).not.toBe('');
|
|
106
|
+
expect(screen.queryByRole('button', { name: 'ver más' })).not.toBeInTheDocument();
|
|
107
|
+
});
|
|
108
|
+
it('shows no "ver más" toggle when the label fits in the clamp', () => {
|
|
109
|
+
// jsdom defaults: scrollHeight === clientHeight === 0 → nothing is clipped
|
|
110
|
+
render(_jsx(Harness, { currentBreakPoint: 1 }));
|
|
111
|
+
expect(screen.queryByRole('button', { name: 'ver más' })).not.toBeInTheDocument();
|
|
112
|
+
});
|
|
113
|
+
it('offers "ver más" when the clamp clips the label, and expands/collapses on click', async () => {
|
|
114
|
+
mockLabelOverflow();
|
|
115
|
+
render(_jsx(Harness, { currentBreakPoint: 1 }));
|
|
116
|
+
const toggle = await screen.findByRole('button', { name: 'ver más' });
|
|
117
|
+
await userEvent.click(toggle);
|
|
118
|
+
expect(getLabelDiv().className).toBe(styles.expandedLbl);
|
|
119
|
+
const collapseToggle = screen.getByRole('button', { name: 'ver menos' });
|
|
120
|
+
await userEvent.click(collapseToggle);
|
|
121
|
+
expect(getLabelDiv().className).toBe(styles.clampLbl);
|
|
122
|
+
});
|
|
123
|
+
});
|
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
import styles from './MaterialCheckBoxStep.module.css';
|
|
3
3
|
import ErkCheckBox from '../../../Shared/ErkCheckbox/ErkCheckBox';
|
|
4
|
-
import React, { useContext } from 'react';
|
|
4
|
+
import React, { useContext, useLayoutEffect, useRef, useState } from 'react';
|
|
5
5
|
import StepComponent from '../../Step';
|
|
6
6
|
import FormContext from '../../../Contexts/FormContext';
|
|
7
|
-
import { useAppSelector } from '../../../hooks';
|
|
7
|
+
import { useAppSelector, selectWidthStats } from '../../../hooks';
|
|
8
8
|
import { useFormStep } from '../../StepHooks';
|
|
9
9
|
import StepFillerContainer from '../../Utils/@StepFiller/StepFiller';
|
|
10
10
|
import MaterialInputContainer from '../../Utils/MaterialInputContainer/MaterialInputContainer';
|
|
11
|
-
import { calcStepWidth } from '../../StepFunctions';
|
|
11
|
+
import { calcStepWidth, resolveStepSpan } from '../../StepFunctions';
|
|
12
12
|
function CheckBoxStep({ step, editable }) {
|
|
13
13
|
const { ref, value, onChange, error, field } = useFormStep(step, {
|
|
14
14
|
defaultValue: step.defaultValue ?? false,
|
|
@@ -19,6 +19,26 @@ function CheckBoxStep({ step, editable }) {
|
|
|
19
19
|
});
|
|
20
20
|
const { formStyle, postview } = useAppSelector((state) => state.global);
|
|
21
21
|
const form = useContext(FormContext);
|
|
22
|
+
const widthStats = useAppSelector(selectWidthStats);
|
|
23
|
+
const span = resolveStepSpan(step, widthStats, form.size);
|
|
24
|
+
// when the breakpoint collapses the step to width: 100% (see MaterialInputContainer),
|
|
25
|
+
// the fixed desktop pixel width no longer matches the real available space
|
|
26
|
+
const collapsed = widthStats.currentBreakPoint <= span;
|
|
27
|
+
const labelRef = useRef(null);
|
|
28
|
+
const [expanded, setExpanded] = useState(false);
|
|
29
|
+
const [clamped, setClamped] = useState(false);
|
|
30
|
+
useLayoutEffect(() => {
|
|
31
|
+
if (!collapsed || expanded)
|
|
32
|
+
return;
|
|
33
|
+
const measure = () => {
|
|
34
|
+
const label = labelRef.current;
|
|
35
|
+
if (label)
|
|
36
|
+
setClamped(label.scrollHeight > label.clientHeight);
|
|
37
|
+
};
|
|
38
|
+
measure();
|
|
39
|
+
window.addEventListener('resize', measure);
|
|
40
|
+
return () => window.removeEventListener('resize', measure);
|
|
41
|
+
}, [collapsed, expanded, step.label]);
|
|
22
42
|
const renderNested = () => {
|
|
23
43
|
const steps = value ? step.steps : step.uncheckedSteps;
|
|
24
44
|
return (_jsx(React.Fragment, { children: steps?.map((idStep) => {
|
|
@@ -27,12 +47,19 @@ function CheckBoxStep({ step, editable }) {
|
|
|
27
47
|
}) }));
|
|
28
48
|
};
|
|
29
49
|
const renderLabel = (colon = false) => {
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
50
|
+
const label = `${step.label}${step.required ? ' *' : ''}${colon ? ':' : ''}`;
|
|
51
|
+
if (!collapsed) {
|
|
52
|
+
return (_jsx("div", { className: span === 4 ? styles.checkboxLbl : styles.overflowLbl, style: {
|
|
53
|
+
maxWidth: calcStepWidth(span, form.size) - 45,
|
|
54
|
+
}, children: label }));
|
|
55
|
+
}
|
|
56
|
+
return (_jsxs("div", { className: styles.labelColumn, children: [_jsx("div", { ref: labelRef, className: expanded ? styles.expandedLbl : styles.clampLbl, children: label }), (clamped || expanded) && (_jsx("button", { type: "button", className: styles.toggleLbl, style: { color: formStyle.primaryColor }, onClick: (event) => {
|
|
57
|
+
event.stopPropagation();
|
|
58
|
+
setExpanded(!expanded);
|
|
59
|
+
}, children: expanded ? 'ver menos' : 'ver más' }))] }));
|
|
33
60
|
};
|
|
34
61
|
return (_jsxs(StepFillerContainer, { step: step, children: [_jsx(MaterialInputContainer, { step: step, editable: editable, maxHeight: step.size < 4, children: _jsxs("div", { className: styles.container, children: [_jsxs("div", { className: styles.labelCheckBoxContainer, style: {
|
|
35
|
-
height:
|
|
62
|
+
height: span < 4 && !collapsed ? 31 : undefined,
|
|
36
63
|
minHeight: 31,
|
|
37
64
|
alignItems: step.isList ? 'start' : 'center',
|
|
38
65
|
}, children: [step.label && !step.isList && renderLabel(true), _jsx(ErkCheckBox, { ...field, padding: "0px", inputRef: ref, error: !!error, checked: value, onChange: onChange, "data-testid": step.id, readOnly: !editable || postview }), step.label && step.isList && renderLabel()] }), (step.description ?? !!error) && (_jsx("div", { className: styles.descriptionPar, style: {
|
|
@@ -14,6 +14,36 @@
|
|
|
14
14
|
overflow: hidden;
|
|
15
15
|
text-overflow: ellipsis;
|
|
16
16
|
}
|
|
17
|
+
.labelColumn {
|
|
18
|
+
display: flex;
|
|
19
|
+
flex-direction: column;
|
|
20
|
+
align-items: flex-start;
|
|
21
|
+
flex: 1 1 auto;
|
|
22
|
+
min-width: 0;
|
|
23
|
+
margin-right: 10px;
|
|
24
|
+
margin-left: 10px;
|
|
25
|
+
}
|
|
26
|
+
.clampLbl,
|
|
27
|
+
.expandedLbl {
|
|
28
|
+
font-size: 18px;
|
|
29
|
+
}
|
|
30
|
+
.clampLbl {
|
|
31
|
+
display: -webkit-box;
|
|
32
|
+
-webkit-box-orient: vertical;
|
|
33
|
+
-webkit-line-clamp: 2;
|
|
34
|
+
line-clamp: 2;
|
|
35
|
+
overflow: hidden;
|
|
36
|
+
}
|
|
37
|
+
.toggleLbl {
|
|
38
|
+
background: none;
|
|
39
|
+
border: none;
|
|
40
|
+
padding: 0;
|
|
41
|
+
margin-top: 2px;
|
|
42
|
+
font-size: 13px;
|
|
43
|
+
font-weight: 700;
|
|
44
|
+
font-family: inherit;
|
|
45
|
+
cursor: pointer;
|
|
46
|
+
}
|
|
17
47
|
.labelCheckBoxContainer {
|
|
18
48
|
display: flex;
|
|
19
49
|
align-items: center;
|
|
@@ -1,16 +1,17 @@
|
|
|
1
1
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
2
|
import styles from './MaterialPhoneInputStep.module.css';
|
|
3
|
-
import { calcStepWidth } from '../../StepFunctions';
|
|
3
|
+
import { calcStepWidth, resolveStepSpan } from '../../StepFunctions';
|
|
4
4
|
import { useContext } from 'react';
|
|
5
5
|
import FormContext from '../../../Contexts/FormContext';
|
|
6
|
-
import {
|
|
6
|
+
import { selectWidthStats, useAppSelector } from '../../../hooks';
|
|
7
7
|
import { useFormStep } from '../../StepHooks';
|
|
8
8
|
import ErkPhoneInput from '../../../Shared/ErkPhoneInput/EkrPhoneInput';
|
|
9
9
|
import { isEmptyPhoneNumber, isValidPhoneNumber } from '../../../Utils/PhoneFunctions';
|
|
10
10
|
function PhoneInputStep({ step, editable, defaultValue = step.defaultValue }) {
|
|
11
|
-
const
|
|
11
|
+
const widthStats = useAppSelector(selectWidthStats);
|
|
12
12
|
const postview = useAppSelector((state) => state.global.postview);
|
|
13
13
|
const form = useContext(FormContext);
|
|
14
|
+
const span = resolveStepSpan(step, widthStats, form.size);
|
|
14
15
|
const { ref, value, onChange, error, field } = useFormStep(step, {
|
|
15
16
|
defaultValue: defaultValue ?? '',
|
|
16
17
|
debounce: true,
|
|
@@ -26,7 +27,7 @@ function PhoneInputStep({ step, editable, defaultValue = step.defaultValue }) {
|
|
|
26
27
|
},
|
|
27
28
|
});
|
|
28
29
|
return (_jsx("div", { className: styles.container, style: {
|
|
29
|
-
width: currentBreakPoint <=
|
|
30
|
+
width: widthStats.currentBreakPoint <= span ? '100%' : calcStepWidth(span, form.size),
|
|
30
31
|
minHeight: step.description || (!postview && editable) ? '55px' : '43px',
|
|
31
32
|
}, children: _jsx(ErkPhoneInput, { ...field, value: value, inputRef: ref, error: !!error, labelMargin: 0, label: step.label, onChange: onChange, "data-testid": step.id, required: step.required, readOnly: !editable || postview, helperText: error?.message ?? step.description }) }));
|
|
32
33
|
}
|
|
@@ -3,11 +3,25 @@ import { ApiSelector, CheckBox, ClassifierSelector, EntityValuePicker, FormSelec
|
|
|
3
3
|
import { AYFFormStep } from '../@Types/AYFFormStep';
|
|
4
4
|
import { CBRFormStep } from '../@Types/CBRFormStep';
|
|
5
5
|
import { Condition } from '../@Types/Condition';
|
|
6
|
-
import { DependencyStore } from '../Form/Form';
|
|
6
|
+
import { DependencyStore, WidthStats } from '../Form/Form';
|
|
7
|
+
import { GBaseStep } from '../@Types/GenericFormSteps';
|
|
7
8
|
import { Entity } from '../@Types/Entity';
|
|
8
9
|
export type FillerSteps = FormSelector | ClassifierSelector | EntityValuePicker | ApiSelector | CheckBox;
|
|
9
10
|
export declare function calcFillerSize(step: FillerSteps, steps: Record<string, FormStep>, values: Record<string, unknown>, size: FormSize): number;
|
|
10
11
|
export declare const calcStepWidth: (stepSize: 1 | 2 | 3 | 4, size: FormSize) => number;
|
|
12
|
+
/** Column span of a step inside the form grid (the grid itself has at most 4 columns). */
|
|
13
|
+
export type FormSpan = 1 | 2 | 3 | 4;
|
|
14
|
+
/** Steps that occupy form-grid columns; content-only steps (e.g. titles) may omit `size`. */
|
|
15
|
+
interface SpannedStep {
|
|
16
|
+
size?: FormSpan;
|
|
17
|
+
sizes?: GBaseStep['sizes'];
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Effective column span of a step at the current width: its explicit `sizes` override for the
|
|
21
|
+
* screen when present (clamped to that screen's columns), the derived legacy default otherwise.
|
|
22
|
+
* Desktop never reads `sizes` — its source of truth stays the legacy `size` field.
|
|
23
|
+
*/
|
|
24
|
+
export declare function resolveStepSpan(step: SpannedStep, widthStats: Pick<WidthStats, 'currentBreakPoint' | 'isMobile'>, formSize: FormSize): FormSpan;
|
|
11
25
|
export declare const calcDefaultValue: (step: FormStep | CBRFormStep) => any;
|
|
12
26
|
export declare const iterateNestedSteps: (idStep: string, steps: Record<string, FormStep>, iteration: (step: FormStep, path: string[]) => void, path?: string[]) => void;
|
|
13
27
|
/**
|
|
@@ -25,3 +39,4 @@ export declare const evaluateCondition: (condition: Condition, dependencies: Dep
|
|
|
25
39
|
} & Record<string, any>;
|
|
26
40
|
}) => boolean;
|
|
27
41
|
export declare function getRelativeDate(nDays?: number, nHours?: number, nMinutes?: number, subtract?: boolean, date?: Date): Date;
|
|
42
|
+
export {};
|
|
@@ -97,6 +97,64 @@ function recursivelyCheckOpenSize(idStep, steps, values) {
|
|
|
97
97
|
export const calcStepWidth = (stepSize, size) => {
|
|
98
98
|
return size.blockSize * stepSize + size.spacingSize * (stepSize - 1);
|
|
99
99
|
};
|
|
100
|
+
const clampSpan = (span, columns) => Math.max(1, Math.min(span, columns));
|
|
101
|
+
/**
|
|
102
|
+
* Columns the form grid has on each screen — the editor's model, mirrored so both sides agree on
|
|
103
|
+
* what fits: desktop uses the form's configured `blockNum`, tablet caps it at 2, and mobile is the
|
|
104
|
+
* single column this renderer has always collapsed to.
|
|
105
|
+
*/
|
|
106
|
+
function columnsForBreakpoint(formSize, breakpoint) {
|
|
107
|
+
switch (breakpoint) {
|
|
108
|
+
case 'desktop':
|
|
109
|
+
return formSize.blockNum;
|
|
110
|
+
case 'tablet':
|
|
111
|
+
return clampSpan(formSize.blockNum, 2);
|
|
112
|
+
case 'mobile':
|
|
113
|
+
return 1;
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
/**
|
|
117
|
+
* Span a step gets on a screen when it has no explicit override — the legacy model translated to
|
|
118
|
+
* the responsive one: desktop keeps the step's `size` (falling back to full width, as steps
|
|
119
|
+
* without one have always rendered), and the other screens shrink that span to fit their column
|
|
120
|
+
* count.
|
|
121
|
+
*/
|
|
122
|
+
function deriveDefaultSpan(step, breakpoint, formSize) {
|
|
123
|
+
const desktopSpan = clampSpan(step.size ?? formSize.blockNum, formSize.blockNum);
|
|
124
|
+
if (breakpoint === 'desktop')
|
|
125
|
+
return desktopSpan;
|
|
126
|
+
return clampSpan(desktopSpan, columnsForBreakpoint(formSize, breakpoint));
|
|
127
|
+
}
|
|
128
|
+
/**
|
|
129
|
+
* Maps the renderer's width model onto the editor's. The editor lays out exactly three screens,
|
|
130
|
+
* while `currentBreakPoint` counts how many columns currently fit, so the two need a bridge:
|
|
131
|
+
* `isMobile` is the editor's mobile screen, and because the editor's tablet layout is a 2-column
|
|
132
|
+
* grid (see `columnsForBreakpoint`) its overrides apply only once at most 2 columns fit.
|
|
133
|
+
* Reduced-but-wider states (e.g. 3 columns of a 4-column form) keep the desktop spans — the
|
|
134
|
+
* editor has no screen for them, and this keeps forms without overrides rendering exactly as
|
|
135
|
+
* they always have.
|
|
136
|
+
*/
|
|
137
|
+
function breakpointForWidthStats(widthStats, formSize) {
|
|
138
|
+
if (widthStats.isMobile)
|
|
139
|
+
return 'mobile';
|
|
140
|
+
if (widthStats.currentBreakPoint < formSize.blockNum && widthStats.currentBreakPoint <= 2)
|
|
141
|
+
return 'tablet';
|
|
142
|
+
return 'desktop';
|
|
143
|
+
}
|
|
144
|
+
/**
|
|
145
|
+
* Effective column span of a step at the current width: its explicit `sizes` override for the
|
|
146
|
+
* screen when present (clamped to that screen's columns), the derived legacy default otherwise.
|
|
147
|
+
* Desktop never reads `sizes` — its source of truth stays the legacy `size` field.
|
|
148
|
+
*/
|
|
149
|
+
export function resolveStepSpan(step, widthStats, formSize) {
|
|
150
|
+
const breakpoint = breakpointForWidthStats(widthStats, formSize);
|
|
151
|
+
if (breakpoint !== 'desktop') {
|
|
152
|
+
const override = step.sizes?.[breakpoint];
|
|
153
|
+
if (override !== undefined)
|
|
154
|
+
return clampSpan(override, columnsForBreakpoint(formSize, breakpoint));
|
|
155
|
+
}
|
|
156
|
+
return deriveDefaultSpan(step, breakpoint, formSize);
|
|
157
|
+
}
|
|
100
158
|
export const calcDefaultValue = (step) => {
|
|
101
159
|
switch (step.type) {
|
|
102
160
|
case FormStepTypes.CHECKBOX:
|
|
@@ -2,7 +2,7 @@ import AYFFormStepTypes from '../constants/AYFFormStepTypes';
|
|
|
2
2
|
import CBRFormStepTypes from '../constants/CBRFormStepTypes';
|
|
3
3
|
import FormStepTypes, { ApiSelectorOptionTypes, ClassifierOptionTypes, EntityValueOptionTypes, OptionTypes, } from '../constants/FormStepTypes';
|
|
4
4
|
import { describe, expect, test, vi } from 'vitest';
|
|
5
|
-
import { calcDefaultValue, calcFillerSize, calcStepWidth, calcSubSteps, iterateNestedSteps } from './StepFunctions';
|
|
5
|
+
import { calcDefaultValue, calcFillerSize, calcStepWidth, calcSubSteps, iterateNestedSteps, resolveStepSpan, } from './StepFunctions';
|
|
6
6
|
import { stringToDraft } from '../Utils/DraftFunctions';
|
|
7
7
|
describe('StepFunctions', function () {
|
|
8
8
|
describe('calcFillerSize', function () {
|
|
@@ -411,6 +411,37 @@ describe('StepFunctions', function () {
|
|
|
411
411
|
expect(size).toBe(440);
|
|
412
412
|
});
|
|
413
413
|
});
|
|
414
|
+
describe('resolveStepSpan', function () {
|
|
415
|
+
const fourColumns = { blockNum: 4, blockSize: 210, spacingSize: 20 };
|
|
416
|
+
const desktop = { currentBreakPoint: 4, isMobile: false };
|
|
417
|
+
const threeColumns = { currentBreakPoint: 3, isMobile: false };
|
|
418
|
+
const tablet = { currentBreakPoint: 2, isMobile: false };
|
|
419
|
+
const mobile = { currentBreakPoint: 1, isMobile: true };
|
|
420
|
+
test('Legacy step derives every screen from the scalar size', async () => {
|
|
421
|
+
const step = { size: 3 };
|
|
422
|
+
expect(resolveStepSpan(step, desktop, fourColumns)).toBe(3);
|
|
423
|
+
expect(resolveStepSpan(step, tablet, fourColumns)).toBe(2);
|
|
424
|
+
expect(resolveStepSpan(step, mobile, fourColumns)).toBe(1);
|
|
425
|
+
});
|
|
426
|
+
test('Step without size falls back to full width', async () => {
|
|
427
|
+
expect(resolveStepSpan({}, desktop, fourColumns)).toBe(4);
|
|
428
|
+
expect(resolveStepSpan({}, tablet, fourColumns)).toBe(2);
|
|
429
|
+
});
|
|
430
|
+
test('Tablet override applies once at most 2 columns fit', async () => {
|
|
431
|
+
const step = { size: 2, sizes: { tablet: 1 } };
|
|
432
|
+
expect(resolveStepSpan(step, tablet, fourColumns)).toBe(1);
|
|
433
|
+
expect(resolveStepSpan(step, desktop, fourColumns)).toBe(2);
|
|
434
|
+
});
|
|
435
|
+
test('Reduced-but-wider states keep the desktop span', async () => {
|
|
436
|
+
const step = { size: 3, sizes: { tablet: 1 } };
|
|
437
|
+
expect(resolveStepSpan(step, threeColumns, fourColumns)).toBe(3);
|
|
438
|
+
});
|
|
439
|
+
test('Overrides are clamped to the columns of their screen', async () => {
|
|
440
|
+
const step = { size: 1, sizes: { tablet: 4, mobile: 3 } };
|
|
441
|
+
expect(resolveStepSpan(step, tablet, fourColumns)).toBe(2);
|
|
442
|
+
expect(resolveStepSpan(step, mobile, fourColumns)).toBe(1);
|
|
443
|
+
});
|
|
444
|
+
});
|
|
414
445
|
describe('calcDefaultValue', function () {
|
|
415
446
|
test('CheckBox', async () => {
|
|
416
447
|
const defaultValue = calcDefaultValue({
|
|
@@ -1,15 +1,16 @@
|
|
|
1
1
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
2
|
import ErkTextField from '../../../Shared/ErkTextField/ErkTextField';
|
|
3
3
|
import styles from './MaterialTextInputStep.module.css';
|
|
4
|
-
import { calcStepWidth } from '../../StepFunctions';
|
|
4
|
+
import { calcStepWidth, resolveStepSpan } from '../../StepFunctions';
|
|
5
5
|
import { useContext } from 'react';
|
|
6
6
|
import FormContext from '../../../Contexts/FormContext';
|
|
7
|
-
import {
|
|
7
|
+
import { selectWidthStats, useAppSelector } from '../../../hooks';
|
|
8
8
|
import { useFormStep } from '../../StepHooks';
|
|
9
9
|
function TextInputStep({ step, editable, maxLength, IconComponent, defaultValue = step.defaultValue, validation = step.validation, }) {
|
|
10
|
-
const
|
|
10
|
+
const widthStats = useAppSelector(selectWidthStats);
|
|
11
11
|
const { postview } = useAppSelector((state) => state.global);
|
|
12
12
|
const form = useContext(FormContext);
|
|
13
|
+
const span = resolveStepSpan(step, widthStats, form.size);
|
|
13
14
|
const { ref, value, onChange, error, field } = useFormStep(step, {
|
|
14
15
|
defaultValue: defaultValue ?? '',
|
|
15
16
|
debounce: true,
|
|
@@ -27,7 +28,7 @@ function TextInputStep({ step, editable, maxLength, IconComponent, defaultValue
|
|
|
27
28
|
},
|
|
28
29
|
});
|
|
29
30
|
return (_jsx("div", { className: styles.container, style: {
|
|
30
|
-
width: currentBreakPoint <=
|
|
31
|
+
width: widthStats.currentBreakPoint <= span ? '100%' : calcStepWidth(span, form.size),
|
|
31
32
|
minHeight: step.description || (!postview && editable && (step.required || validation)) ? '55px' : '43px',
|
|
32
33
|
}, children: _jsx(ErkTextField, { ...field, value: value, inputRef: ref, labelMargin: 0, error: !!error, label: step.label, onChange: onChange, maxLength: maxLength, "data-testid": step.id, required: step.required, readOnly: !editable || postview, helperText: error?.message ?? step.description, IconComponent: step.showIcon ? IconComponent : undefined }) }));
|
|
33
34
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
-
import { calcStepWidth } from '../../StepFunctions';
|
|
2
|
+
import { calcStepWidth, resolveStepSpan } from '../../StepFunctions';
|
|
3
3
|
import styles from './MaterialTitleStep.module.css';
|
|
4
4
|
import { useAppSelector } from '../../../hooks';
|
|
5
5
|
import FormContext from '../../../Contexts/FormContext';
|
|
@@ -16,7 +16,7 @@ function TitleStep({ step }) {
|
|
|
16
16
|
const { formStyle } = useAppSelector((state) => state.global);
|
|
17
17
|
const form = useContext(FormContext);
|
|
18
18
|
const widthStats = useAppSelector((state) => state.widthStats);
|
|
19
|
-
const
|
|
19
|
+
const span = resolveStepSpan(step, widthStats, form.size);
|
|
20
20
|
const title = useMemo(() => calcBaseDraft(value?.title ?? step.title), //Usa por defecto lo que venga en el value, pero no escucha cambios
|
|
21
21
|
[step.title, value?.title]);
|
|
22
22
|
const sourceTitle = useMemo(() => calcBaseDraft(step.title), [step.title]);
|
|
@@ -36,9 +36,9 @@ function TitleStep({ step }) {
|
|
|
36
36
|
const path = useMemo(() => [...step.stepPath, step.id].join('.'), [step.stepPath, step.id]);
|
|
37
37
|
return (_jsxs("div", { className: styles.container, style: {
|
|
38
38
|
color: formStyle.textColor,
|
|
39
|
-
width: widthStats.currentBreakPoint <=
|
|
39
|
+
width: widthStats.currentBreakPoint <= span ? '100%' : calcStepWidth(span, form.size),
|
|
40
40
|
}, children: [_jsx("div", { className: styles.titleLbl, style: {
|
|
41
|
-
textAlign: widthStats.isMobile && widthStats.currentBreakPoint <=
|
|
41
|
+
textAlign: widthStats.isMobile && widthStats.currentBreakPoint <= span ? 'center' : 'start',
|
|
42
42
|
}, children: _jsx(SmartDraftRenderer, { draft: title, sourceDraft: sourceTitle, dependencies: values, property: `${path}.title`, onChange: (title) => onChange({ ...value, title }) }) }), _jsx("div", { className: styles.descriptionPar, children: _jsx(SmartDraftRenderer, { draft: description, sourceDraft: sourceDescription, dependencies: values, margin: title ? '10px 0px' : '0px 0px 5px 0px', property: `${path}.description`, onChange: (description) => onChange({ ...value, description }) }) })] }));
|
|
43
43
|
}
|
|
44
44
|
export default TitleStep;
|
|
@@ -1,23 +1,24 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
2
|
import { useCallback, useContext, useState } from 'react';
|
|
3
3
|
import { useFormContext } from 'react-hook-form';
|
|
4
|
-
import { calcFillerSize } from '../../StepFunctions';
|
|
4
|
+
import { calcFillerSize, resolveStepSpan } from '../../StepFunctions';
|
|
5
5
|
import FormContext from '../../../Contexts/FormContext';
|
|
6
6
|
import styles from './StepFiller.module.css';
|
|
7
|
-
import {
|
|
7
|
+
import { selectWidthStats, useAppSelector } from '../../../hooks';
|
|
8
8
|
import { SizeChangeContext } from './StepFillerContext';
|
|
9
9
|
function StepFiller({ step, children }) {
|
|
10
10
|
const { getValues } = useFormContext();
|
|
11
11
|
const form = useContext(FormContext);
|
|
12
12
|
const [fillerSize, setFillerSize] = useState(calcFillerSize(step, form.steps, getValues(), form.size));
|
|
13
|
-
const
|
|
13
|
+
const widthStats = useAppSelector(selectWidthStats);
|
|
14
|
+
const span = resolveStepSpan(step, widthStats, form.size);
|
|
14
15
|
const handleSizeChange = useCallback(() => {
|
|
15
16
|
setFillerSize(calcFillerSize(step, form.steps, getValues(), form.size));
|
|
16
17
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
17
18
|
}, []);
|
|
18
19
|
if (step.maxSize && step.maxSize < form.size.blockNum) {
|
|
19
20
|
return (_jsxs("div", { className: styles.firstLvlContainer, style: {
|
|
20
|
-
width: currentBreakPoint <=
|
|
21
|
+
width: widthStats.currentBreakPoint <= span ? '100%' : 'fit-content',
|
|
21
22
|
}, children: [_jsx(SizeChangeContext.Provider, { value: handleSizeChange, children: children }), fillerSize > 0 && (_jsx("div", { className: styles.smallSeparator, style: {
|
|
22
23
|
width: fillerSize,
|
|
23
24
|
} }))] }));
|
|
@@ -1,15 +1,16 @@
|
|
|
1
1
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
2
|
import { useContext } from 'react';
|
|
3
3
|
import FormContext from '../../../Contexts/FormContext';
|
|
4
|
-
import { useAppSelector,
|
|
5
|
-
import { calcStepWidth } from '../../StepFunctions';
|
|
4
|
+
import { useAppSelector, selectWidthStats } from '../../../hooks';
|
|
5
|
+
import { calcStepWidth, resolveStepSpan } from '../../StepFunctions';
|
|
6
6
|
import styles from './MaterialInputContainer.module.css';
|
|
7
7
|
function MaterialInputContainer({ step, editable, children, onClick, maxHeight = true, }) {
|
|
8
8
|
const form = useContext(FormContext);
|
|
9
|
-
const
|
|
9
|
+
const widthStats = useAppSelector(selectWidthStats);
|
|
10
10
|
const postview = useAppSelector((state) => state.global.postview);
|
|
11
|
+
const span = resolveStepSpan(step, widthStats, form.size);
|
|
11
12
|
return (_jsx("div", { className: styles.container, style: {
|
|
12
|
-
width: currentBreakPoint <=
|
|
13
|
+
width: widthStats.currentBreakPoint <= span ? '100%' : calcStepWidth(span, form.size),
|
|
13
14
|
minHeight: maxHeight
|
|
14
15
|
? step.description || (!postview && editable && step.required)
|
|
15
16
|
? '55px'
|
|
@@ -1,19 +1,17 @@
|
|
|
1
1
|
import CBRFormStepTypes from '../constants/CBRFormStepTypes';
|
|
2
2
|
export const calcCbrForm = (form, formValues) => {
|
|
3
|
-
/** Callers pass the form straight out of `createNextState`, whose result Immer deep-freezes. */
|
|
4
|
-
const steps = { ...form.steps };
|
|
5
3
|
for (const step of Object.values(form.steps)) {
|
|
6
4
|
if (step.type === CBRFormStepTypes.CBR_INCIDENCIAS) {
|
|
7
5
|
const incidentStep = step;
|
|
8
6
|
const incValue = formValues[incidentStep.id ?? ''];
|
|
9
7
|
if (incValue) {
|
|
10
8
|
for (const incident of incValue) {
|
|
11
|
-
handleAddIncident(incident, incidentStep, steps, formValues);
|
|
9
|
+
handleAddIncident(incident, incidentStep, form.steps, formValues);
|
|
12
10
|
}
|
|
13
11
|
}
|
|
14
12
|
}
|
|
15
13
|
}
|
|
16
|
-
return
|
|
14
|
+
return form;
|
|
17
15
|
};
|
|
18
16
|
const handleAddIncident = (incident, step, steps, formValues) => {
|
|
19
17
|
const spaceStep = {
|
package/dist/hooks.d.ts
CHANGED
|
@@ -5,4 +5,5 @@ import { QueryActionCreatorResult, QueryDefinition, ResultTypeFrom } from '@redu
|
|
|
5
5
|
export declare const useAppDispatch: () => AppDispatch;
|
|
6
6
|
export declare const useAppSelector: TypedUseSelectorHook<RootState>;
|
|
7
7
|
export declare const selectBreakPoint: (state: RootState) => number;
|
|
8
|
+
export declare const selectWidthStats: (state: RootState) => RootState["widthStats"];
|
|
8
9
|
export declare function useApiSubscribe(): <D extends QueryDefinition<any, any, any, any>>(subscription: ThunkAction<QueryActionCreatorResult<D>, any, any, any>) => Promise<ResultTypeFrom<D> | undefined>;
|
package/dist/hooks.js
CHANGED
|
@@ -20,6 +20,7 @@ export const useAppSelector = (selector, options) => {
|
|
|
20
20
|
return useSelector((state) => selector(state.forms[idForm] ?? defaultRootState), options);
|
|
21
21
|
};
|
|
22
22
|
export const selectBreakPoint = (state) => state.widthStats.currentBreakPoint;
|
|
23
|
+
export const selectWidthStats = (state) => state.widthStats;
|
|
23
24
|
export function useApiSubscribe() {
|
|
24
25
|
const dispatch = useAppDispatch();
|
|
25
26
|
const subscriptions = useRef([]);
|
package/package.json
CHANGED
package/dist/App/App.cbr.test.js
DELETED
|
@@ -1,78 +0,0 @@
|
|
|
1
|
-
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
-
import { render, screen, waitFor } from '@testing-library/react';
|
|
3
|
-
import { describe, expect, it } from 'vitest';
|
|
4
|
-
import '@testing-library/jest-dom';
|
|
5
|
-
import App from './App';
|
|
6
|
-
import CBRFormStepTypes from '../constants/CBRFormStepTypes';
|
|
7
|
-
const INCIDENT = {
|
|
8
|
-
idSpaceStep: 'CBR_TIPO_ESPACIO-PYDqn',
|
|
9
|
-
idElementStep: 'CBR_LOCATIVAS-fp6xL',
|
|
10
|
-
idCommentStep: 'CBR_COMENTARIO-sQjpP',
|
|
11
|
-
num: 1,
|
|
12
|
-
};
|
|
13
|
-
const ID_SUB_ELEMENT_STEP = 'CBR_LOCATIVAS-G4S0r';
|
|
14
|
-
/** Shaped after a real posventa ticket: incidents are stored flat, keyed by generated step ids. */
|
|
15
|
-
const POSVENTA_VALUES = {
|
|
16
|
-
CBR_INCIDENCIAS: [INCIDENT],
|
|
17
|
-
[INCIDENT.idSpaceStep]: { id: '35365', label: 'Zona de Oficios' },
|
|
18
|
-
[INCIDENT.idCommentStep]: 'GRAN HUMEDAD!!!',
|
|
19
|
-
[INCIDENT.idElementStep]: { id: '06', label: 'CIELOS', subStep: ID_SUB_ELEMENT_STEP },
|
|
20
|
-
[ID_SUB_ELEMENT_STEP]: { id: '0601', label: 'Cielo Falso' },
|
|
21
|
-
};
|
|
22
|
-
const POSVENTA_FORM = {
|
|
23
|
-
name: 'Formulario PosVenta',
|
|
24
|
-
firstSection: 'FIRST',
|
|
25
|
-
sections: {
|
|
26
|
-
FIRST: { id: 'FIRST', name: '', nextSection: null, steps: ['CBR_INCIDENCIAS'] },
|
|
27
|
-
},
|
|
28
|
-
steps: {
|
|
29
|
-
CBR_INCIDENCIAS: {
|
|
30
|
-
id: 'CBR_INCIDENCIAS',
|
|
31
|
-
type: CBRFormStepTypes.CBR_INCIDENCIAS,
|
|
32
|
-
label: 'Incidencias',
|
|
33
|
-
description: null,
|
|
34
|
-
addBtnLabel: 'Agregar Incidencia',
|
|
35
|
-
required: true,
|
|
36
|
-
unitLabel: 'Incidencia',
|
|
37
|
-
dependencies: ['CBR_PROYECTO'],
|
|
38
|
-
spaceStep: {
|
|
39
|
-
label: 'Espacio',
|
|
40
|
-
description: null,
|
|
41
|
-
size: 1,
|
|
42
|
-
required: true,
|
|
43
|
-
showIcon: true,
|
|
44
|
-
searchable: true,
|
|
45
|
-
},
|
|
46
|
-
commentStep: { label: 'Comentario', description: null, size: 1, required: true },
|
|
47
|
-
elementStep: {
|
|
48
|
-
label: 'Elemento',
|
|
49
|
-
description: null,
|
|
50
|
-
size: 1,
|
|
51
|
-
required: true,
|
|
52
|
-
showIcon: true,
|
|
53
|
-
searchable: true,
|
|
54
|
-
subStep: {
|
|
55
|
-
label: 'Elemento Hijo',
|
|
56
|
-
description: null,
|
|
57
|
-
size: 1,
|
|
58
|
-
required: true,
|
|
59
|
-
showIcon: true,
|
|
60
|
-
searchable: true,
|
|
61
|
-
},
|
|
62
|
-
},
|
|
63
|
-
},
|
|
64
|
-
},
|
|
65
|
-
size: { blockNum: 4, blockSize: 210, spacingSize: 20 },
|
|
66
|
-
};
|
|
67
|
-
describe('CBR posventa postview', () => {
|
|
68
|
-
it('renders the submitted incidents read-only', async () => {
|
|
69
|
-
const { container } = render(_jsx(App, { formData: POSVENTA_FORM, valuesData: POSVENTA_VALUES, idOrganization: 'melendez', postview: true, internal: true, isWidget: true, editable: false }));
|
|
70
|
-
await waitFor(() => {
|
|
71
|
-
expect(container.textContent).toContain('Incidencia 1');
|
|
72
|
-
});
|
|
73
|
-
expect(container.textContent).toContain('GRAN HUMEDAD!!!');
|
|
74
|
-
expect(screen.getByDisplayValue('Zona de Oficios')).toBeInTheDocument();
|
|
75
|
-
expect(screen.getByDisplayValue('CIELOS')).toBeInTheDocument();
|
|
76
|
-
expect(screen.getByDisplayValue('Cielo Falso')).toBeInTheDocument();
|
|
77
|
-
});
|
|
78
|
-
});
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|
|
@@ -1,123 +0,0 @@
|
|
|
1
|
-
import { createNextState } from '@reduxjs/toolkit';
|
|
2
|
-
import { describe, expect, test } from 'vitest';
|
|
3
|
-
import CBRFormStepTypes from '../constants/CBRFormStepTypes';
|
|
4
|
-
import { calcCbrForm } from './CBRFunctions';
|
|
5
|
-
const INCIDENT = {
|
|
6
|
-
idSpaceStep: 'CBR_TIPO_ESPACIO-space1',
|
|
7
|
-
idElementStep: 'CBR_LOCATIVAS-element1',
|
|
8
|
-
idCommentStep: 'CBR_COMENTARIO-comment1',
|
|
9
|
-
num: 1,
|
|
10
|
-
};
|
|
11
|
-
/** Second element level, reached through the element value's `subStep` pointer. */
|
|
12
|
-
const ID_SUB_ELEMENT_STEP = 'CBR_LOCATIVAS-subElement1';
|
|
13
|
-
const FORM_VALUES = {
|
|
14
|
-
CBR_INCIDENCIAS: [INCIDENT],
|
|
15
|
-
[INCIDENT.idSpaceStep]: { id: '35362', label: 'Sala' },
|
|
16
|
-
[INCIDENT.idCommentStep]: 'GRAN HUMEDAD',
|
|
17
|
-
[INCIDENT.idElementStep]: {
|
|
18
|
-
id: '06',
|
|
19
|
-
label: 'CIELOS',
|
|
20
|
-
subStep: ID_SUB_ELEMENT_STEP,
|
|
21
|
-
},
|
|
22
|
-
[ID_SUB_ELEMENT_STEP]: { id: '0601', label: 'Cielo Falso' },
|
|
23
|
-
};
|
|
24
|
-
function buildForm() {
|
|
25
|
-
return {
|
|
26
|
-
name: 'Formulario PosVenta',
|
|
27
|
-
firstSection: 'FIRST',
|
|
28
|
-
sections: {
|
|
29
|
-
FIRST: {
|
|
30
|
-
id: 'FIRST',
|
|
31
|
-
name: '',
|
|
32
|
-
nextSection: null,
|
|
33
|
-
steps: ['CBR_INCIDENCIAS'],
|
|
34
|
-
},
|
|
35
|
-
},
|
|
36
|
-
steps: {
|
|
37
|
-
CBR_INCIDENCIAS: {
|
|
38
|
-
id: 'CBR_INCIDENCIAS',
|
|
39
|
-
type: CBRFormStepTypes.CBR_INCIDENCIAS,
|
|
40
|
-
label: 'Incidencias',
|
|
41
|
-
description: null,
|
|
42
|
-
addBtnLabel: 'Agregar',
|
|
43
|
-
required: true,
|
|
44
|
-
unitLabel: 'Incidencia',
|
|
45
|
-
dependencies: ['CBR_PROYECTO'],
|
|
46
|
-
spaceStep: {
|
|
47
|
-
label: 'Espacio',
|
|
48
|
-
description: null,
|
|
49
|
-
size: 1,
|
|
50
|
-
required: true,
|
|
51
|
-
showIcon: true,
|
|
52
|
-
searchable: true,
|
|
53
|
-
},
|
|
54
|
-
commentStep: {
|
|
55
|
-
label: 'Comentario',
|
|
56
|
-
description: null,
|
|
57
|
-
size: 1,
|
|
58
|
-
required: true,
|
|
59
|
-
},
|
|
60
|
-
elementStep: {
|
|
61
|
-
label: 'Elemento',
|
|
62
|
-
description: null,
|
|
63
|
-
size: 1,
|
|
64
|
-
required: true,
|
|
65
|
-
showIcon: true,
|
|
66
|
-
searchable: true,
|
|
67
|
-
subStep: {
|
|
68
|
-
label: 'Elemento Hijo',
|
|
69
|
-
description: null,
|
|
70
|
-
size: 1,
|
|
71
|
-
required: true,
|
|
72
|
-
showIcon: true,
|
|
73
|
-
searchable: true,
|
|
74
|
-
},
|
|
75
|
-
},
|
|
76
|
-
},
|
|
77
|
-
},
|
|
78
|
-
size: { blockNum: 4, blockSize: 210, spacingSize: 20 },
|
|
79
|
-
};
|
|
80
|
-
}
|
|
81
|
-
describe('calcCbrForm', function () {
|
|
82
|
-
test('expands every incident into its own space, comment and element steps', () => {
|
|
83
|
-
const result = calcCbrForm(buildForm(), FORM_VALUES);
|
|
84
|
-
expect(result.steps[INCIDENT.idSpaceStep]).toMatchObject({
|
|
85
|
-
id: INCIDENT.idSpaceStep,
|
|
86
|
-
type: CBRFormStepTypes.CBR_TIPO_ESPACIO,
|
|
87
|
-
});
|
|
88
|
-
expect(result.steps[INCIDENT.idCommentStep]).toMatchObject({
|
|
89
|
-
id: INCIDENT.idCommentStep,
|
|
90
|
-
type: CBRFormStepTypes.CBR_COMENTARIO,
|
|
91
|
-
});
|
|
92
|
-
expect(result.steps[INCIDENT.idElementStep]).toMatchObject({
|
|
93
|
-
id: INCIDENT.idElementStep,
|
|
94
|
-
type: CBRFormStepTypes.CBR_LOCATIVAS,
|
|
95
|
-
subStep: ID_SUB_ELEMENT_STEP,
|
|
96
|
-
});
|
|
97
|
-
expect(result.steps[ID_SUB_ELEMENT_STEP]).toMatchObject({
|
|
98
|
-
id: ID_SUB_ELEMENT_STEP,
|
|
99
|
-
type: CBRFormStepTypes.CBR_LOCATIVAS,
|
|
100
|
-
});
|
|
101
|
-
});
|
|
102
|
-
test('leaves the form it was given untouched', () => {
|
|
103
|
-
const form = buildForm();
|
|
104
|
-
calcCbrForm(form, FORM_VALUES);
|
|
105
|
-
expect(Object.keys(form.steps)).toEqual(['CBR_INCIDENCIAS']);
|
|
106
|
-
});
|
|
107
|
-
/**
|
|
108
|
-
* The caller hands the form straight out of `createNextState`, whose result Immer deep-freezes, so
|
|
109
|
-
* expanding the incidents by writing into that same `steps` object throws and leaves the form stuck
|
|
110
|
-
* loading forever.
|
|
111
|
-
*/
|
|
112
|
-
test('expands a form frozen by createNextState', () => {
|
|
113
|
-
const frozenForm = createNextState(buildForm(), (draft) => {
|
|
114
|
-
draft.steps.CBR_INCIDENCIAS.idSection = 'FIRST';
|
|
115
|
-
});
|
|
116
|
-
expect(Object.isFrozen(frozenForm.steps)).toBe(true);
|
|
117
|
-
const result = calcCbrForm(frozenForm, FORM_VALUES);
|
|
118
|
-
expect(result.steps[INCIDENT.idSpaceStep]).toBeDefined();
|
|
119
|
-
expect(result.steps[INCIDENT.idCommentStep]).toBeDefined();
|
|
120
|
-
expect(result.steps[INCIDENT.idElementStep]).toBeDefined();
|
|
121
|
-
expect(result.steps[ID_SUB_ELEMENT_STEP]).toBeDefined();
|
|
122
|
-
});
|
|
123
|
-
});
|
/package/dist/{App/App.cbr.test.d.ts → FormSteps/CheckBoxStep/CheckBoxStep.mobileLabel.test.d.ts}
RENAMED
|
File without changes
|