@bigbinary/neeto-form-frontend 4.4.10 → 4.4.11

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/README.md CHANGED
@@ -7,7 +7,7 @@ nano exports `@bigbinary/neeto-form-frontend` NPM package and
7
7
  See
8
8
  [this page](https://github.com/search?q=org%3Abigbinary+%22neeto-form-engine+%28%22&type=code)
9
9
  to find host applications using this nano. Changes made in the nano should be
10
- rolled out to all of them, especially migrations.
10
+ rolled out to all of them, especially migrations..
11
11
 
12
12
  # Contents
13
13
 
@@ -65,8 +65,6 @@ model. It also stores submissions.
65
65
  mount NeetoFormEngine::Engine, at: "/neeto_form"
66
66
  ```
67
67
 
68
- **NOTE: The mount point must be `/neeto_form` and cannot be changed to any
69
- other path.**
70
68
 
71
69
  6. Create file `neeto_form_engine.rb` under `config/initializers` to provide the
72
70
  `owner_class` information
@@ -144,7 +144,7 @@
144
144
  "questionType": "Question type",
145
145
  "hideQuestionHelpDescription": "When enabled, this question will not be visible in the created form. It can be used to track \"reference ids\" and UTM parameters.",
146
146
  "responseVisibleOnlyToHostHelpDescription": "When enabled, the response to this question will only be visible to the host",
147
- "readOnlyHelpDescription": "When enabled, the client will only be able to see the answer of this question, but will not be able to change the answer of this question. Since you are marking this question as \"read-only\" you should pre-fill the question with an answer.",
147
+ "readOnlyHelpDescription": "When enabled, the client will only be able to see the answer of this question, but will not be able to change the answer of this question. Since you are marking this question as \"read-only\". You should pre-fill the question with an answer.",
148
148
  "fitImagesHelpDescription": "When enabled, images zoom to fill the entire space (some edges may get cut off). When disabled, the complete image is shown (may have empty space around it).",
149
149
  "verifyHuman": "Verify that you are a human",
150
150
  "pictureChoiceSettings": "Picture choice settings",
@@ -154,6 +154,14 @@
154
154
  }
155
155
  }
156
156
  },
157
+ "textarea": {
158
+ "minimumWords": "Minimum number of words",
159
+ "minimumWordsHelpDescription": "User must type specified number of words otherwise user will not be able to submit the form.",
160
+ "minimumWordsError_one": "Minimum {{count}} word is required.",
161
+ "minimumWordsError_other": "Minimum {{count}} words are required.",
162
+ "minimumWordsMinError": "Minimum number of words must be 1 or higher.",
163
+ "minimumWordsMaxError": "Number of words can't be higher than 10,000."
164
+ },
157
165
  "opinionScale": {
158
166
  "showLabels": "Show labels",
159
167
  "leftLabel": "Left label",
package/dist/.ready ADDED
@@ -0,0 +1 @@
1
+ Built at 2026-03-26T11:40:05.395Z
package/dist/BuildForm.js CHANGED
@@ -4,7 +4,7 @@ import { isPresent, truncate, findBy, isNotPresent, slugify, existsBy, findById,
4
4
  import Spinner from '@bigbinary/neetoui/Spinner';
5
5
  import { equals, path, when, assoc, includes, isEmpty, reject, keys, pick, omit, evolve, map, mergeLeft, isNil, pipe, filter, isNotNil, values, uniq, join, pathSatisfies, assocPath, dissoc, append, pluck, test, paths, difference, prop, startsWith, split, last, either, F, T } from 'ramda';
6
6
  import { k as useDeleteQuestion, Q as QUESTION_TYPES, b as useForm, l as useCreateQuestion, m as useUpdateQuestion, C as CAPTCHA_TYPES, n as useReorderQuestions, i as QUERY_KEYS } from './constants-Cj0XxE8a.js';
7
- import { j as QUESTION_ACTIONS, k as INDEPENDENT_LABELS_MAP, l as buildDisplayLabel, m as isElementOverflowing, n as Drag, o as SELECTABLE_KINDS, p as isAutoGeneratedQuestion, q as isRichTextQuestion, r as SPOT_QUESTION_FIELD_CODE, N as NON_BASIC_LATIN_CHARACTERS_REGEX, s as RESERVED_FIELD_CODES, t as renameKey, f as LABEL_FIELDS, u as isDefaultLanguage, v as buildAddQuestionButtonProps, w as validateEditorContent, F as FIELD_CODES_REJECT_CHARS_REGEX, x as isImmutableField, Q as QUESTIONS_WITHOUT_FIELD_CODE, y as NON_HIDEABLE_FIELDS, z as NON_READ_ONLY_FIELDS, O as OPINION_LABEL_MAX_LENGTH, B as RICH_TEXT_QUESTIONS, e as ADDRESS_FIELD_WIDTHS, A as ADDRESS_FIELD_TYPES, G as generateDefaultOptions, M as MINIMUM_ADDRESS_FIELDS, H as getDeletedRecords, J as randomId, K as getActiveRecords, T as FILE_TYPES_MAP, U as FILE_GROUPS, V as OPINION_SCALE_MIN_VALUE_OPTIONS, W as OPINION_SCALE_MAX_VALUE_OPTIONS, D as DEFAULT_CHOICE_QUESTION_ATTRIBUTES, X as MINIMUM_OPTIONS, Y as IMAGE_HEIGHT_SLIDER_DEFAULTS, Z as RATING_OPTIONS, _ as STAR_RATING_MIN_VALUE_OPTIONS, $ as STAR_RATING_MAX_VALUE_OPTIONS, a0 as STAR_RATING_ICONS_MAP, a1 as buildDisabledAddButtonHelpPopoverProps, a2 as buildReorderPayload, a3 as QUESTIONS_INITIAL_VALUE, a4 as isMandatoryField, a5 as QUESTION_KINDS, a as QUESTION_KIND, a6 as getActiveQuestionKindDetails, a7 as DEFAULT_AVAILABLE_LANGUAGES, a8 as DEFAULT_ADVANCED_FEATURES } from './yup-CSYHMEsr.js';
7
+ import { j as QUESTION_ACTIONS, k as INDEPENDENT_LABELS_MAP, l as buildDisplayLabel, m as isElementOverflowing, n as Drag, o as SELECTABLE_KINDS, p as isAutoGeneratedQuestion, q as isRichTextQuestion, r as SPOT_QUESTION_FIELD_CODE, N as NON_BASIC_LATIN_CHARACTERS_REGEX, s as RESERVED_FIELD_CODES, t as renameKey, f as LABEL_FIELDS, u as isDefaultLanguage, v as buildAddQuestionButtonProps, w as validateEditorContent, F as FIELD_CODES_REJECT_CHARS_REGEX, M as MIN_WORDS_SWITCH_LABEL_PROPS, x as MIN_WORDS, y as isImmutableField, Q as QUESTIONS_WITHOUT_FIELD_CODE, z as NON_HIDEABLE_FIELDS, B as NON_READ_ONLY_FIELDS, O as OPINION_LABEL_MAX_LENGTH, G as RICH_TEXT_QUESTIONS, e as ADDRESS_FIELD_WIDTHS, A as ADDRESS_FIELD_TYPES, H as generateDefaultOptions, J as MINIMUM_ADDRESS_FIELDS, K as getDeletedRecords, T as randomId, U as getActiveRecords, V as FILE_TYPES_MAP, W as FILE_GROUPS, X as OPINION_SCALE_MIN_VALUE_OPTIONS, Y as OPINION_SCALE_MAX_VALUE_OPTIONS, D as DEFAULT_CHOICE_QUESTION_ATTRIBUTES, Z as MINIMUM_OPTIONS, _ as IMAGE_HEIGHT_SLIDER_DEFAULTS, $ as RATING_OPTIONS, a0 as STAR_RATING_MIN_VALUE_OPTIONS, a1 as STAR_RATING_MAX_VALUE_OPTIONS, a2 as STAR_RATING_ICONS_MAP, a3 as buildDisabledAddButtonHelpPopoverProps, a4 as buildReorderPayload, a5 as QUESTIONS_INITIAL_VALUE, a6 as isMandatoryField, a7 as QUESTION_KINDS, a as QUESTION_KIND, a8 as getActiveQuestionKindDetails, a9 as DEFAULT_AVAILABLE_LANGUAGES, aa as DEFAULT_ADVANCED_FEATURES } from './yup-B3JGBWm7.js';
8
8
  import _slicedToArray from '@babel/runtime/helpers/slicedToArray';
9
9
  import { useState, useRef, useEffect, createElement } from 'react';
10
10
  import { Droppable, Draggable, DragDropContext } from '@hello-pangea/dnd';
@@ -35,9 +35,10 @@ import { useFormikContext, useField } from 'formik';
35
35
  import Accordion from '@bigbinary/neetoui/Accordion';
36
36
  import Select from '@bigbinary/neetoui/formik/Select';
37
37
  import Switch from '@bigbinary/neetoui/formik/Switch';
38
- import Textarea from '@bigbinary/neetoui/formik/Textarea';
38
+ import Textarea$1 from '@bigbinary/neetoui/formik/Textarea';
39
39
  import NeetoEditor from '@bigbinary/neeto-editor/Editor';
40
40
  import Input from '@bigbinary/neetoui/formik/Input';
41
+ import Switch$1 from '@bigbinary/neetoui/Switch';
41
42
  import { isEditorEmpty } from '@bigbinary/neeto-editor/utils';
42
43
  import * as yup from 'yup';
43
44
  import OptionFields from '@bigbinary/neeto-molecules/OptionFields';
@@ -661,7 +662,9 @@ var INITIAL_VALUES = {
661
662
  kind: "",
662
663
  label: "",
663
664
  fieldCode: "",
664
- isSplitMode: false
665
+ isSplitMode: false,
666
+ minWords: null,
667
+ isMinWordsEnabled: false
665
668
  };
666
669
  var VALID_FIELD_CODE_REGEX = /^[a-z0-9]+(_[a-z0-9]+)*$/;
667
670
  var DRAFT_ID_PREFIX = "draft-";
@@ -691,7 +694,7 @@ var FormikAdaptiveInput = function FormikAdaptiveInput(_ref) {
691
694
  var end = element.value.length;
692
695
  element.setSelectionRange(end, end);
693
696
  };
694
- return /*#__PURE__*/jsx(Textarea, _objectSpread$g({
697
+ return /*#__PURE__*/jsx(Textarea$1, _objectSpread$g({
695
698
  label: label,
696
699
  name: name,
697
700
  className: "neeto-form-nano-adaptive-input",
@@ -992,6 +995,38 @@ var FieldCode = function FieldCode() {
992
995
  });
993
996
  };
994
997
 
998
+ var Textarea = function Textarea() {
999
+ var _useTranslation = useTranslation(),
1000
+ t = _useTranslation.t;
1001
+ var _useField = useField("minWords"),
1002
+ _useField2 = _slicedToArray(_useField, 3),
1003
+ setMinWords = _useField2[2].setValue;
1004
+ var _useField3 = useField("isMinWordsEnabled"),
1005
+ _useField4 = _slicedToArray(_useField3, 3),
1006
+ isMinWordsEnabled = _useField4[0].value,
1007
+ setIsMinWordsEnabled = _useField4[2].setValue;
1008
+ var handleToggleChange = function handleToggleChange() {
1009
+ var isEnabled = !isMinWordsEnabled;
1010
+ setIsMinWordsEnabled(isEnabled);
1011
+ setMinWords(isEnabled ? MIN_WORDS.DEFAULT : null);
1012
+ };
1013
+ return /*#__PURE__*/jsxs("div", {
1014
+ className: "flex flex-col gap-2",
1015
+ children: [/*#__PURE__*/jsx(Switch$1, {
1016
+ checked: isMinWordsEnabled,
1017
+ label: t("neetoForm.questions.textarea.minimumWords"),
1018
+ labelProps: MIN_WORDS_SWITCH_LABEL_PROPS,
1019
+ onChange: handleToggleChange
1020
+ }), isMinWordsEnabled && /*#__PURE__*/jsx(Input, {
1021
+ className: "w-42 ms-14",
1022
+ max: MIN_WORDS.MAX,
1023
+ min: MIN_WORDS.MIN,
1024
+ name: "minWords",
1025
+ type: "number"
1026
+ })]
1027
+ });
1028
+ };
1029
+
995
1030
  var Form$1 = function Form(_ref) {
996
1031
  var questions = _ref.questions,
997
1032
  initialFocusRef = _ref.initialFocusRef,
@@ -1035,7 +1070,8 @@ var Form$1 = function Form(_ref) {
1035
1070
  usesCustomSubmissionComponent = _useBuildFormStore.usesCustomSubmissionComponent;
1036
1071
  var isFieldCodeEnabled = advancedFeatures.fieldCode,
1037
1072
  markResponsesAsReadOnly = advancedFeatures.markResponsesAsReadOnly,
1038
- restrictResponseVisibilityToHost = advancedFeatures.restrictResponseVisibilityToHost;
1073
+ restrictResponseVisibilityToHost = advancedFeatures.restrictResponseVisibilityToHost,
1074
+ enforceMinWordsInMultiLineTexts = advancedFeatures.enforceMinWordsInMultiLineTexts;
1039
1075
  var kind = values.kind;
1040
1076
  var questionKinds = isEdit ? allQuestionKinds : availableQuestionKinds;
1041
1077
  var questionKindUniquenessPattern = mergeLeft(isEdit && kindUniqueOn ? kindUniqueOn(values) : {}, {
@@ -1062,8 +1098,9 @@ var Form$1 = function Form(_ref) {
1062
1098
  var shouldShowHideSwitch = isFieldCodeEnabled && !usesCustomSubmissionComponent && !includes(kind, NON_HIDEABLE_FIELDS);
1063
1099
  var canRestrictResponseVisibilityToHost = restrictResponseVisibilityToHost && !includes(kind, NON_HIDEABLE_FIELDS);
1064
1100
  var canMarkResponsesAsReadOnly = markResponsesAsReadOnly && !includes(kind, NON_READ_ONLY_FIELDS);
1101
+ var isTextarea = kind === QUESTION_TYPES.TEXTAREA;
1065
1102
  var isBasicSettingsVisible = !isRequired && (!isImmutable || shouldShowHideSwitch);
1066
- var isSettingsBlockVisible = kind !== QUESTION_TYPES.SPOT && (isBasicSettingsVisible || canMarkResponsesAsReadOnly);
1103
+ var isSettingsBlockVisible = kind !== QUESTION_TYPES.SPOT && (isBasicSettingsVisible || canMarkResponsesAsReadOnly || isTextarea);
1067
1104
  var handleKindChange = function handleKindChange(option) {
1068
1105
  var data = buildQuestionData(option);
1069
1106
  updateFormState(data);
@@ -1142,7 +1179,7 @@ var Form$1 = function Form(_ref) {
1142
1179
  label: t("neetoForm.common.readOnly"),
1143
1180
  labelProps: readOnlySwitchLabelProps(readOnlyHelpDocUrl),
1144
1181
  name: "isReadOnly"
1145
- })]
1182
+ }), isTextarea && enforceMinWordsInMultiLineTexts && /*#__PURE__*/jsx(Textarea, {})]
1146
1183
  }), shouldShowFieldCode && /*#__PURE__*/jsx(Accordion, {
1147
1184
  className: "neeto-form-nano-advanced-properties-accordion",
1148
1185
  "data-testid": "advanced-properties-card",
@@ -1232,6 +1269,11 @@ var formValidationSchema = function formValidationSchema(question) {
1232
1269
  })
1233
1270
  })) : schema.notRequired();
1234
1271
  }),
1272
+ minWords: yup.number().nullable().when(["kind", "isMinWordsEnabled"], function (kind, isMinWordsEnabled, schema) {
1273
+ if (kind !== QUESTION_TYPES.TEXTAREA) return schema.notRequired();
1274
+ if (!isMinWordsEnabled) return schema.notRequired();
1275
+ return schema.typeError(t("neetoForm.questions.textarea.minimumWordsMinError")).required(t("neetoForm.questions.textarea.minimumWordsMinError")).min(MIN_WORDS.MIN, t("neetoForm.questions.textarea.minimumWordsMinError")).max(MIN_WORDS.MAX, t("neetoForm.questions.textarea.minimumWordsMaxError"));
1276
+ }),
1235
1277
  highestRatingLabel: yup.string().nullable(),
1236
1278
  averageRatingLabel: yup.string().nullable(),
1237
1279
  lowestRatingLabel: yup.string().nullable(),
@@ -1380,7 +1422,9 @@ var Edit = function Edit(_ref) {
1380
1422
  })
1381
1423
  }), /*#__PURE__*/jsx(Form$2, {
1382
1424
  formikProps: {
1383
- initialValues: question,
1425
+ initialValues: _objectSpread$b(_objectSpread$b({}, question), {}, {
1426
+ isMinWordsEnabled: isPresent(question.minWords)
1427
+ }),
1384
1428
  validationSchema: formValidationSchema(question, isFieldCodeEnabled),
1385
1429
  onSubmit: handleSubmit
1386
1430
  },
@@ -2523,7 +2567,8 @@ var StarRating = function StarRating() {
2523
2567
  });
2524
2568
  };
2525
2569
 
2526
- var _excluded$1 = ["fields"];
2570
+ var _excluded$1 = ["minWords"],
2571
+ _excluded2 = ["fields"];
2527
2572
  function ownKeys$2(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; }
2528
2573
  function _objectSpread$2(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys$2(Object(t), true).forEach(function (r) { _defineProperty(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys$2(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; }
2529
2574
  var CHECKBOX = QUESTION_TYPES.CHECKBOX,
@@ -2643,19 +2688,24 @@ var readOnlySwitchLabelProps = function readOnlySwitchLabelProps(helpUrl) {
2643
2688
  }
2644
2689
  };
2645
2690
  };
2646
- var transformValues = function transformValues(values) {
2647
- switch (values.kind) {
2691
+ var transformValues = function transformValues(_ref4) {
2692
+ var minWords = _ref4.minWords,
2693
+ values = _objectWithoutProperties(_ref4, _excluded$1);
2694
+ var rest = omit(["isMinWordsEnabled"], values);
2695
+ switch (rest.kind) {
2648
2696
  case QUESTION_TYPES.ADDRESS:
2649
2697
  {
2650
- var _values$fields = values.fields,
2651
- fields = _values$fields === void 0 ? [] : _values$fields,
2652
- rest = _objectWithoutProperties(values, _excluded$1);
2653
- return _objectSpread$2(_objectSpread$2({}, rest), {}, {
2698
+ var _rest$fields = rest.fields,
2699
+ fields = _rest$fields === void 0 ? [] : _rest$fields,
2700
+ addressRest = _objectWithoutProperties(rest, _excluded2);
2701
+ return _objectSpread$2(_objectSpread$2({}, addressRest), {}, {
2654
2702
  fieldsAttributes: map(renameKey("deleted", "_destroy"), omitDraftId(fields))
2655
2703
  });
2656
2704
  }
2657
2705
  default:
2658
- return values;
2706
+ return _objectSpread$2(_objectSpread$2({}, rest), {}, {
2707
+ minWords: minWords !== null && minWords !== void 0 ? minWords : ""
2708
+ });
2659
2709
  }
2660
2710
  };
2661
2711