@arquimedes.co/eureka-forms 3.0.57-new-steps → 3.0.57

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.
Files changed (120) hide show
  1. package/dist/@Types/FormStep.d.ts +2 -7
  2. package/dist/@Types/FormStep.js +1 -2
  3. package/dist/@Types/GenericFormSteps.d.ts +7 -0
  4. package/dist/App/App.cbr.test.js +78 -0
  5. package/dist/App/App.d.ts +6 -1
  6. package/dist/App/AppHooks.js +2 -2
  7. package/dist/FormSteps/CheckBoxStep/CheckBoxStep.mobileLabel.test.js +123 -0
  8. package/dist/FormSteps/CheckBoxStep/MaterialCheckBoxStep/MaterialCheckBoxStep.js +34 -7
  9. package/dist/FormSteps/CheckBoxStep/MaterialCheckBoxStep/MaterialCheckBoxStep.module.css +30 -0
  10. package/dist/FormSteps/MapperStep/MaterialMapperStep/Element/MapperElementComponent.js +1 -1
  11. package/dist/FormSteps/PhoneInputStep/MaterialTextInputStep/MaterialPhoneInputStep.js +5 -4
  12. package/dist/FormSteps/Step.js +4 -19
  13. package/dist/FormSteps/StepFunctions.d.ts +16 -1
  14. package/dist/FormSteps/StepFunctions.js +58 -14
  15. package/dist/FormSteps/StepFunctions.test.js +32 -55
  16. package/dist/FormSteps/TextInputStep/MaterialTextInputStep/MaterialTextInputStep.js +5 -4
  17. package/dist/FormSteps/TitleStep/MaterialTitleStep/MaterialTitleStep.js +4 -4
  18. package/dist/FormSteps/Utils/@StepFiller/StepFiller.js +5 -4
  19. package/dist/FormSteps/Utils/MaterialInputContainer/MaterialInputContainer.js +5 -4
  20. package/dist/Shared/ErkDatePicker/ErkDatePicker.js +7 -5
  21. package/dist/Shared/ErkDateRangePicker/ErkDateRangePicker.d.ts +3 -2
  22. package/dist/Shared/ErkDateRangePicker/ErkDateRangePicker.js +5 -4
  23. package/dist/Utils/CBRFunctions.js +4 -2
  24. package/dist/Utils/CBRFunctions.test.d.ts +1 -0
  25. package/dist/Utils/CBRFunctions.test.js +123 -0
  26. package/dist/Utils/MaterialProviders.js +6 -0
  27. package/dist/constants/FormStepTypes.d.ts +1 -5
  28. package/dist/constants/FormStepTypes.js +0 -4
  29. package/dist/hooks.d.ts +1 -0
  30. package/dist/hooks.js +1 -0
  31. package/dist/index.lib.d.ts +2 -4
  32. package/dist/index.lib.js +1 -3
  33. package/package.json +7 -8
  34. package/dist/@Types/AvailabilityFormStep.d.ts +0 -48
  35. package/dist/@Types/AvailabilityFormStep.js +0 -24
  36. package/dist/@Types/BankAccountFormStep.d.ts +0 -67
  37. package/dist/@Types/BankAccountFormStep.js +0 -14
  38. package/dist/@Types/BookingFormStep.d.ts +0 -166
  39. package/dist/@Types/BookingFormStep.js +0 -44
  40. package/dist/@Types/CalendarFormStep.d.ts +0 -209
  41. package/dist/@Types/CalendarFormStep.js +0 -55
  42. package/dist/@Types/CommunicationChannelFormStep.d.ts +0 -51
  43. package/dist/@Types/CommunicationChannelFormStep.js +0 -15
  44. package/dist/@Types/ConsentFormStep.d.ts +0 -36
  45. package/dist/@Types/ConsentFormStep.js +0 -8
  46. package/dist/@Types/EntityFormFormStep.d.ts +0 -66
  47. package/dist/@Types/EntityFormFormStep.js +0 -20
  48. package/dist/@Types/EventFormStep.d.ts +0 -52
  49. package/dist/@Types/EventFormStep.js +0 -13
  50. package/dist/@Types/ExternalReferenceFormStep.d.ts +0 -74
  51. package/dist/@Types/ExternalReferenceFormStep.js +0 -20
  52. package/dist/@Types/LocationFormStep.d.ts +0 -159
  53. package/dist/@Types/LocationFormStep.js +0 -46
  54. package/dist/@Types/NumericFormSteps.d.ts +0 -108
  55. package/dist/@Types/NumericFormSteps.js +0 -20
  56. package/dist/@Types/PredefinedSelectorFormStep.d.ts +0 -51
  57. package/dist/@Types/PredefinedSelectorFormStep.js +0 -39
  58. package/dist/@Types/StepRef.d.ts +0 -135
  59. package/dist/@Types/StepRef.js +0 -49
  60. package/dist/FormSteps/AvailabilityStep/AvailabilityStep.d.ts +0 -7
  61. package/dist/FormSteps/AvailabilityStep/AvailabilityStep.js +0 -14
  62. package/dist/FormSteps/AvailabilityStep/MaterialAvailabilityStep/MaterialAvailabilityStep.d.ts +0 -8
  63. package/dist/FormSteps/AvailabilityStep/MaterialAvailabilityStep/MaterialAvailabilityStep.js +0 -32
  64. package/dist/FormSteps/BookingStep/BookingStep.d.ts +0 -17
  65. package/dist/FormSteps/BookingStep/BookingStep.js +0 -14
  66. package/dist/FormSteps/BookingStep/MaterialBookingStep/MaterialBookingStep.d.ts +0 -7
  67. package/dist/FormSteps/BookingStep/MaterialBookingStep/MaterialBookingStep.js +0 -32
  68. package/dist/FormSteps/LocationStep/LocationStep.d.ts +0 -10
  69. package/dist/FormSteps/LocationStep/LocationStep.js +0 -14
  70. package/dist/FormSteps/LocationStep/MaterialLocationStep/MaterialLocationStep.d.ts +0 -3
  71. package/dist/FormSteps/LocationStep/MaterialLocationStep/MaterialLocationStep.js +0 -54
  72. package/dist/FormSteps/LocationStep/MaterialLocationStep/MaterialLocationStep.module.css +0 -18
  73. package/dist/FormSteps/NumberStep/MaterialNumberStep/MaterialNumberStep.d.ts +0 -3
  74. package/dist/FormSteps/NumberStep/MaterialNumberStep/MaterialNumberStep.js +0 -168
  75. package/dist/FormSteps/NumberStep/MaterialNumberStep/MaterialNumberStep.module.css +0 -94
  76. package/dist/FormSteps/NumberStep/NumberStep.d.ts +0 -10
  77. package/dist/FormSteps/NumberStep/NumberStep.js +0 -14
  78. package/dist/Icons/AddIcon.d.ts +0 -3
  79. package/dist/Icons/AddIcon.js +0 -7
  80. package/dist/Icons/EditIcon.d.ts +0 -3
  81. package/dist/Icons/EditIcon.js +0 -7
  82. package/dist/Icons/RemoveIcon.d.ts +0 -3
  83. package/dist/Icons/RemoveIcon.js +0 -7
  84. package/dist/Icons/ViewDayIcon.d.ts +0 -3
  85. package/dist/Icons/ViewDayIcon.js +0 -7
  86. package/dist/Icons/ViewWeekIcon.d.ts +0 -3
  87. package/dist/Icons/ViewWeekIcon.js +0 -7
  88. package/dist/Shared/ErkAddressAutocomplete/ErkAddressAutocomplete.d.ts +0 -19
  89. package/dist/Shared/ErkAddressAutocomplete/ErkAddressAutocomplete.js +0 -80
  90. package/dist/Shared/ErkAddressAutocomplete/ErkAddressAutocomplete.module.css +0 -82
  91. package/dist/Shared/ErkAddressAutocomplete/googlePlaces.d.ts +0 -29
  92. package/dist/Shared/ErkAddressAutocomplete/googlePlaces.js +0 -139
  93. package/dist/Shared/ErkAvailabilityPicker/ErkAvailabilityPicker.d.ts +0 -25
  94. package/dist/Shared/ErkAvailabilityPicker/ErkAvailabilityPicker.js +0 -112
  95. package/dist/Shared/ErkAvailabilityPicker/ErkAvailabilityPicker.module.css +0 -88
  96. package/dist/Shared/ErkAvailabilityPicker/ErkAvailabilityPicker.test.js +0 -75
  97. package/dist/Shared/ErkBookingPicker/ErkBookingPicker.d.ts +0 -31
  98. package/dist/Shared/ErkBookingPicker/ErkBookingPicker.js +0 -223
  99. package/dist/Shared/ErkBookingPicker/ErkBookingPicker.module.css +0 -608
  100. package/dist/Shared/ErkBookingPicker/ErkBookingPicker.stories.d.ts +0 -28
  101. package/dist/Shared/ErkBookingPicker/ErkBookingPicker.stories.js +0 -117
  102. package/dist/Shared/ErkBookingPicker/ErkBookingPicker.stories.test.js +0 -49
  103. package/dist/Shared/ErkBookingPicker/ErkBookingPicker.test.d.ts +0 -1
  104. package/dist/Shared/ErkBookingPicker/ErkBookingPicker.test.js +0 -107
  105. package/dist/Shared/ErkLocationField/ErkLocationField.d.ts +0 -24
  106. package/dist/Shared/ErkLocationField/ErkLocationField.js +0 -125
  107. package/dist/Shared/ErkLocationField/ErkLocationField.module.css +0 -153
  108. package/dist/Shared/ErkLocationField/ErkLocationField.test.d.ts +0 -1
  109. package/dist/Shared/ErkLocationField/ErkLocationField.test.js +0 -82
  110. package/dist/Shared/ErkLocationField/addressFields.d.ts +0 -12
  111. package/dist/Shared/ErkLocationField/addressFields.js +0 -34
  112. package/dist/Shared/ErkLocationField/locationRestrictions.d.ts +0 -6
  113. package/dist/Shared/ErkLocationField/locationRestrictions.js +0 -51
  114. package/dist/Shared/ErkLocationInput/ErkLocationInput.d.ts +0 -15
  115. package/dist/Shared/ErkLocationInput/ErkLocationInput.js +0 -502
  116. package/dist/Shared/ErkLocationInput/ErkLocationInput.module.css +0 -115
  117. package/dist/Shared/ErkNumberField/ErkNumberField.d.ts +0 -42
  118. package/dist/Shared/ErkNumberField/ErkNumberField.js +0 -170
  119. /package/dist/{Shared/ErkAvailabilityPicker/ErkAvailabilityPicker.test.d.ts → App/App.cbr.test.d.ts} +0 -0
  120. /package/dist/{Shared/ErkBookingPicker/ErkBookingPicker.stories.test.d.ts → FormSteps/CheckBoxStep/CheckBoxStep.mobileLabel.test.d.ts} +0 -0
@@ -5,13 +5,7 @@ import { EurekaDraft } from './Draft/Draft';
5
5
  import { ErkValue } from './ErkValue';
6
6
  import * as GSteps from './GenericFormSteps';
7
7
  import { Time } from './Time';
8
- import { LocationStep } from './LocationFormStep';
9
- import { NumberStep, NumberStepType } from './NumericFormSteps';
10
- import { BookingStep } from './BookingFormStep';
11
- import { CalendarStep } from './CalendarFormStep';
12
- export type { LocationStep, NumberStep, BookingStep, CalendarStep };
13
- export { NumberStepType };
14
- export type FormStep = Title | TimePicker | Rating | CheckBox | TextArea | TextInput | DatePicker | FileUpload | Separator | FormSelector | ClassifierSelector | Collapsible | EntityValuePicker | ApiSelector | Mapper | DatePickerRange | LocationStep | NumberStep | BookingStep | CalendarStep;
8
+ export type FormStep = Title | TimePicker | Rating | CheckBox | TextArea | TextInput | DatePicker | FileUpload | Separator | FormSelector | ClassifierSelector | Collapsible | EntityValuePicker | ApiSelector | Mapper | DatePickerRange;
15
9
  export interface Title extends GSteps.GBaseStep {
16
10
  type: FormStepTypes.TITLE;
17
11
  title?: EurekaDraft;
@@ -238,3 +232,4 @@ export interface Mapper extends GSteps.GBaseStep {
238
232
  deletable?: boolean;
239
233
  max?: number;
240
234
  }
235
+ export {};
@@ -1,2 +1 @@
1
- import { NumberStepType } from './NumericFormSteps';
2
- export { NumberStepType };
1
+ export {};
@@ -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,78 @@
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
+ });
package/dist/App/App.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { Classifier, Form } from '../@Types/Form';
1
+ import { Classifier, Form, FormStyle } from '../@Types/Form';
2
2
  import './App.css';
3
3
  import { CustomStep, CustomStepProps } from '../FormSteps/CustomStep';
4
4
  import { EditorState } from 'draft-js';
@@ -17,6 +17,11 @@ export interface AppProps {
17
17
  formData?: Form;
18
18
  /** If the widget is usedInternally */
19
19
  internal?: boolean;
20
+ /**
21
+ * Overrides for the built-in internal style — only honored with `internal`, where the form's own
22
+ * style is ignored. Lets the host app (the admin) render the form in its color scheme, e.g. dark mode.
23
+ */
24
+ formStyle?: Partial<FormStyle>;
20
25
  /** If the widget is a postview (view what the user filled)*/
21
26
  postview?: boolean;
22
27
  /** If postview should only show steps with values*/
@@ -12,7 +12,7 @@ import { iterateNestedSteps } from '../FormSteps/StepFunctions';
12
12
  import { createNextState } from '@reduxjs/toolkit';
13
13
  import { BaseConfirmationMessage } from '../Contexts/FormContext';
14
14
  export const useSetupApp = (isEmbedded, props) => {
15
- const { preview, internal, postview, partial, editable, handleConfirmed, customSteps, valuesData, apiKey, formData, classifiers, idCurrentAgent, ...others } = props;
15
+ const { preview, internal, postview, partial, editable, handleConfirmed, customSteps, valuesData, apiKey, formData, classifiers, idCurrentAgent, formStyle: formStyleOverride, ...others } = props;
16
16
  const [form, setForm] = useState(undefined);
17
17
  const [organization, setOrganization] = useState(undefined);
18
18
  const dispatch = useAppDispatch();
@@ -112,7 +112,7 @@ export const useSetupApp = (isEmbedded, props) => {
112
112
  user,
113
113
  apiKey,
114
114
  formStyle: internal
115
- ? InternalFormStyle
115
+ ? { ...InternalFormStyle, ...(formStyleOverride ?? {}) }
116
116
  : {
117
117
  ...InternalFormStyle,
118
118
  ...(form?.style ?? {}),
@@ -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
- return (_jsxs("div", { className: step.size === 4 ? styles.checkboxLbl : styles.overflowLbl, style: {
31
- maxWidth: calcStepWidth(step.size, form.size) - 45,
32
- }, children: [step.label, step.required ? ' *' : '', colon ? ':' : ''] }));
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: step.size < 4 ? 31 : undefined,
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;
@@ -25,7 +25,7 @@ function MapperElementComponent(props) {
25
25
  const mapSubSteps = () => {
26
26
  return (_jsx(CustomContext.Provider, { value: nestedCustomContext, children: step.rootSteps.map((idSubStep) => {
27
27
  const subStep = form.steps[element.ids[idSubStep]];
28
- return (_createElement(StepComponent, { ...props, editable: editable ? (step.editable ?? true) : false, step: subStep, key: element.ids[idSubStep] }));
28
+ return (_createElement(StepComponent, { ...props, editable: editable ? (step.editable ?? true) : false, step: subStep, key: idSubStep }));
29
29
  }) }));
30
30
  };
31
31
  switch (step.style?.type) {
@@ -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 { selectBreakPoint, useAppSelector } from '../../../hooks';
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 currentBreakPoint = useAppSelector(selectBreakPoint);
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 <= step.size ? '100%' : calcStepWidth(step.size, form.size),
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
  }
@@ -1,4 +1,4 @@
1
- import { jsx as _jsx, Fragment as _Fragment } from "react/jsx-runtime";
1
+ import { Fragment as _Fragment, jsx as _jsx } from "react/jsx-runtime";
2
2
  import FormStepTypes from '../constants/FormStepTypes';
3
3
  import SelectorStep from './SelectorStep/SelectorStep';
4
4
  import SeparatorStep from './SeparatorStep/SeparatorStep';
@@ -24,18 +24,15 @@ import CustomContext from '../Contexts/CustomContext';
24
24
  import { selectOriginalValue, useCondition } from './StepHooks';
25
25
  import TimePickerStep from './TimePickerStep/TimePickerStep';
26
26
  import PhoneInputStep from './PhoneInputStep/PhoneInputStep';
27
- import LocationStepComponent from './LocationStep/LocationStep';
28
- import NumberStepComponent from './NumberStep/NumberStep';
29
- import BookingStepComponent from './BookingStep/BookingStep';
30
- import AvailabilityStepComponent from './AvailabilityStep/AvailabilityStep';
31
27
  function StepComponent({ step, ...props }) {
32
28
  const { postview, partial } = useAppSelector((state) => state.global);
33
29
  const { customSteps, customClientInfoStep } = useContext(CustomContext);
34
30
  const originalValue = useAppSelector((state) => selectOriginalValue(state, step));
35
- const showStep = useCondition(step.condition);
31
+ const showStep = useCondition(step?.condition);
32
+ /** A rootSteps entry with no matching step definition must not take the whole form down with it. */
36
33
  if (!step) {
37
34
  console.error('Step not found!', step);
38
- return _jsx("div", {});
35
+ return _jsx(_Fragment, {});
39
36
  }
40
37
  if (!showStep)
41
38
  return _jsx(_Fragment, {});
@@ -78,18 +75,6 @@ function StepComponent({ step, ...props }) {
78
75
  case FormStepTypes.DATEPICKER: {
79
76
  return _jsx(DatePickerStep, { ...props, step: step, editable: editable });
80
77
  }
81
- case FormStepTypes.LOCATION: {
82
- return _jsx(LocationStepComponent, { ...props, step: step, editable: editable });
83
- }
84
- case FormStepTypes.NUMBER: {
85
- return _jsx(NumberStepComponent, { ...props, step: step, editable: editable });
86
- }
87
- case FormStepTypes.BOOKING: {
88
- return _jsx(BookingStepComponent, { ...props, step: step, editable: editable });
89
- }
90
- case FormStepTypes.AVAILABILITY: {
91
- return _jsx(AvailabilityStepComponent, { ...props, step: step, editable: editable });
92
- }
93
78
  case FormStepTypes.DATEPICKER_RANGE: {
94
79
  return _jsx(DatePickerRangeStep, { ...props, step: step, editable: editable });
95
80
  }
@@ -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 {};
@@ -1,4 +1,3 @@
1
- import { NumberStepType, } from '../@Types/FormStep';
2
1
  import FormStepTypes, { ApiSelectorOptionTypes, ClassifierOptionTypes, EntityValueDataTypes, EntityValueOptionTypes, OptionTypes, } from '../constants/FormStepTypes';
3
2
  import CBRFormStepTypes from '../constants/CBRFormStepTypes';
4
3
  import AYFFormStepTypes from '../constants/AYFFormStepTypes';
@@ -88,8 +87,6 @@ function recursivelyCheckOpenSize(idStep, steps, values) {
88
87
  return size;
89
88
  }
90
89
  case FormStepTypes.DATEPICKER:
91
- case FormStepTypes.LOCATION:
92
- case FormStepTypes.NUMBER:
93
90
  case FormStepTypes.TEXTINPUT: {
94
91
  return step.size;
95
92
  }
@@ -100,6 +97,64 @@ function recursivelyCheckOpenSize(idStep, steps, values) {
100
97
  export const calcStepWidth = (stepSize, size) => {
101
98
  return size.blockSize * stepSize + size.spacingSize * (stepSize - 1);
102
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
+ }
103
158
  export const calcDefaultValue = (step) => {
104
159
  switch (step.type) {
105
160
  case FormStepTypes.CHECKBOX:
@@ -120,17 +175,6 @@ export const calcDefaultValue = (step) => {
120
175
  return getRawText();
121
176
  else
122
177
  return '';
123
- case FormStepTypes.LOCATION:
124
- return step.defaultValue ?? null;
125
- case FormStepTypes.NUMBER: {
126
- const numStep = step;
127
- if (numStep.numberType === NumberStepType.MONEY ||
128
- numStep.numberType === NumberStepType.MEASURE ||
129
- numStep.numberType === NumberStepType.UNIT_PRICE) {
130
- return numStep.defaultValue ?? null;
131
- }
132
- return numStep.defaultValue ?? '';
133
- }
134
178
  case FormStepTypes.RATING:
135
179
  case FormStepTypes.DATEPICKER:
136
180
  case FormStepTypes.DATEPICKER_RANGE: