@tellescope/react-components 1.248.0 → 1.249.0

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 (64) hide show
  1. package/lib/cjs/Forms/forms.d.ts +2 -2
  2. package/lib/cjs/Forms/forms.d.ts.map +1 -1
  3. package/lib/cjs/Forms/forms.js +7 -7
  4. package/lib/cjs/Forms/forms.js.map +1 -1
  5. package/lib/cjs/Forms/forms.v2.d.ts +2 -2
  6. package/lib/cjs/Forms/forms.v2.d.ts.map +1 -1
  7. package/lib/cjs/Forms/forms.v2.js +7 -7
  8. package/lib/cjs/Forms/forms.v2.js.map +1 -1
  9. package/lib/cjs/Forms/hooks.d.ts +1 -0
  10. package/lib/cjs/Forms/hooks.d.ts.map +1 -1
  11. package/lib/cjs/Forms/hooks.js +52 -12
  12. package/lib/cjs/Forms/hooks.js.map +1 -1
  13. package/lib/cjs/Forms/inputs.d.ts +1 -1
  14. package/lib/cjs/Forms/inputs.d.ts.map +1 -1
  15. package/lib/cjs/Forms/inputs.js +6 -3
  16. package/lib/cjs/Forms/inputs.js.map +1 -1
  17. package/lib/cjs/Forms/inputs.v2.d.ts +1 -2
  18. package/lib/cjs/Forms/inputs.v2.d.ts.map +1 -1
  19. package/lib/cjs/Forms/inputs.v2.js +1 -44
  20. package/lib/cjs/Forms/inputs.v2.js.map +1 -1
  21. package/lib/cjs/Forms/types.d.ts +1 -0
  22. package/lib/cjs/Forms/types.d.ts.map +1 -1
  23. package/lib/esm/CMS/components.d.ts +1 -0
  24. package/lib/esm/CMS/components.d.ts.map +1 -1
  25. package/lib/esm/Forms/forms.d.ts +4 -4
  26. package/lib/esm/Forms/forms.d.ts.map +1 -1
  27. package/lib/esm/Forms/forms.js +7 -7
  28. package/lib/esm/Forms/forms.js.map +1 -1
  29. package/lib/esm/Forms/forms.v2.d.ts +4 -4
  30. package/lib/esm/Forms/forms.v2.d.ts.map +1 -1
  31. package/lib/esm/Forms/forms.v2.js +7 -7
  32. package/lib/esm/Forms/forms.v2.js.map +1 -1
  33. package/lib/esm/Forms/hooks.d.ts +1 -0
  34. package/lib/esm/Forms/hooks.d.ts.map +1 -1
  35. package/lib/esm/Forms/hooks.js +52 -12
  36. package/lib/esm/Forms/hooks.js.map +1 -1
  37. package/lib/esm/Forms/inputs.d.ts +3 -3
  38. package/lib/esm/Forms/inputs.d.ts.map +1 -1
  39. package/lib/esm/Forms/inputs.js +6 -3
  40. package/lib/esm/Forms/inputs.js.map +1 -1
  41. package/lib/esm/Forms/inputs.native.d.ts +1 -0
  42. package/lib/esm/Forms/inputs.native.d.ts.map +1 -1
  43. package/lib/esm/Forms/inputs.v2.d.ts +1 -2
  44. package/lib/esm/Forms/inputs.v2.d.ts.map +1 -1
  45. package/lib/esm/Forms/inputs.v2.js +1 -44
  46. package/lib/esm/Forms/inputs.v2.js.map +1 -1
  47. package/lib/esm/Forms/types.d.ts +1 -0
  48. package/lib/esm/Forms/types.d.ts.map +1 -1
  49. package/lib/esm/TwilioVideo/hooks.d.ts +1 -1
  50. package/lib/esm/controls.d.ts +2 -2
  51. package/lib/esm/inputs.d.ts +1 -1
  52. package/lib/esm/inputs.native.d.ts +1 -0
  53. package/lib/esm/inputs.native.d.ts.map +1 -1
  54. package/lib/esm/state.d.ts +330 -330
  55. package/lib/esm/theme.native.d.ts +1 -0
  56. package/lib/esm/theme.native.d.ts.map +1 -1
  57. package/lib/tsconfig.tsbuildinfo +1 -1
  58. package/package.json +9 -9
  59. package/src/Forms/forms.tsx +20 -14
  60. package/src/Forms/forms.v2.tsx +12 -6
  61. package/src/Forms/hooks.tsx +58 -9
  62. package/src/Forms/inputs.tsx +6 -3
  63. package/src/Forms/inputs.v2.tsx +1 -50
  64. package/src/Forms/types.ts +1 -0
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tellescope/react-components",
3
- "version": "1.248.0",
3
+ "version": "1.249.0",
4
4
  "description": "",
5
5
  "main": "./lib/cjs/index.js",
6
6
  "module": "./lib/esm/index.js",
@@ -51,13 +51,13 @@
51
51
  "@reduxjs/toolkit": "1.9.0",
52
52
  "@stripe/react-stripe-js": "2.9.0",
53
53
  "@stripe/stripe-js": "1.52.1",
54
- "@tellescope/constants": "1.248.0",
55
- "@tellescope/sdk": "1.248.0",
56
- "@tellescope/types-client": "1.248.0",
57
- "@tellescope/types-models": "1.248.0",
58
- "@tellescope/types-utilities": "1.248.0",
59
- "@tellescope/utilities": "1.248.0",
60
- "@tellescope/validation": "1.248.0",
54
+ "@tellescope/constants": "1.249.0",
55
+ "@tellescope/sdk": "1.249.0",
56
+ "@tellescope/types-client": "1.249.0",
57
+ "@tellescope/types-models": "1.249.0",
58
+ "@tellescope/types-utilities": "1.249.0",
59
+ "@tellescope/utilities": "1.249.0",
60
+ "@tellescope/validation": "1.249.0",
61
61
  "css-to-react-native": "3.0.0",
62
62
  "draft-js": "0.11.7",
63
63
  "draftjs-to-html": "0.9.1",
@@ -84,7 +84,7 @@
84
84
  "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0",
85
85
  "react-native": "^0.65.0 || ^0.66.0 || ^0.67.0 || ^0.68.0 || ^0.71.0"
86
86
  },
87
- "gitHead": "c10ecca2464920b76cda6af60fafbd1c9fde5174",
87
+ "gitHead": "fb54fd85672184ea09d70e3072e6037eedc84822",
88
88
  "publishConfig": {
89
89
  "access": "public"
90
90
  }
@@ -145,6 +145,7 @@ export const QuestionForField = ({
145
145
  uploadingFiles, setUploadingFiles, handleFileUpload,
146
146
  groupFields,
147
147
  AddToDatabase,
148
+ lastNavigationDirectionRef,
148
149
  } : {
149
150
  spacing?: number,
150
151
  form?: Form,
@@ -163,19 +164,19 @@ export const QuestionForField = ({
163
164
  setUploadingFiles: React.Dispatch<React.SetStateAction<{ fieldId: string }[]>>,
164
165
  groupFields?: FormField[],
165
166
  AddToDatabase?: React.JSXElementConstructor<AddToDatabaseProps>,
166
- } & Pick<TellescopeFormProps, "rootResponseId" | "goToNextField" | "groupId" | "groupInstance" | "submit" | "formResponseId" | 'enduserId' | 'isPreviousDisabled' | 'goToPreviousField' | 'enduser' | 'handleDatabaseSelect' | 'onAddFile' | 'onFieldChange' | 'fields' | 'customInputs' | 'responses' | 'selectedFiles' | 'validateField'>) => {
167
+ } & Pick<TellescopeFormProps, "rootResponseId" | "goToNextField" | "groupId" | "groupInstance" | "submit" | "formResponseId" | 'enduserId' | 'isPreviousDisabled' | 'goToPreviousField' | 'enduser' | 'handleDatabaseSelect' | 'onAddFile' | 'onFieldChange' | 'fields' | 'customInputs' | 'responses' | 'selectedFiles' | 'validateField' | 'lastNavigationDirectionRef'>) => {
167
168
  const String = customInputs?.['string'] ?? StringInput
168
169
  const StringLong = customInputs?.['stringLong'] ?? StringLongInput
169
170
  const Email = customInputs?.['email'] ?? EmailInput
170
171
  const Number = customInputs?.['number'] ?? NumberInput
171
- const Phone = customInputs?.['phone'] ?? PhoneInput
172
- const ResolvedDateInput = customInputs?.['date'] ?? DateInput
173
- const Signature = customInputs?.['signature'] ?? SignatureInput
174
- const MultipleChoice = customInputs?.['multiple_choice'] ?? MultipleChoiceInput
175
- const Stripe = customInputs?.['Stripe'] ?? StripeInput
176
- const Chargebee = customInputs?.['Chargebee'] ?? ChargeebeeInput
177
- const File = customInputs?.['file'] ?? FileInput
178
- const Files = customInputs?.['files'] ?? FilesInput
172
+ const Phone = customInputs?.['phone'] ?? PhoneInput
173
+ const ResolvedDateInput = customInputs?.['date'] ?? DateInput
174
+ const Signature = customInputs?.['signature'] ?? SignatureInput
175
+ const MultipleChoice = customInputs?.['multiple_choice'] ?? MultipleChoiceInput
176
+ const Stripe = customInputs?.['Stripe'] ?? StripeInput
177
+ const Chargebee = customInputs?.['Chargebee'] ?? ChargeebeeInput
178
+ const File = customInputs?.['file'] ?? FileInput
179
+ const Files = customInputs?.['files'] ?? FilesInput
179
180
  const Ranking = customInputs?.['ranking'] ?? RankingInput
180
181
  const Rating = customInputs?.['rating'] ?? RatingInput
181
182
  const Address = customInputs?.['Address'] ?? AddressInput
@@ -293,7 +294,7 @@ export const QuestionForField = ({
293
294
  <DateStringInput field={field} disabled={value.disabled} value={value.answer.value as string} onChange={onFieldChange as ChangeHandler<'string'>} form={form} />
294
295
  )
295
296
  : field.type === 'Hidden Value' ? (
296
- <HiddenValue groupFields={groupFields} isSinglePage={isSinglePage} goToNextField={goToNextField} goToPreviousField={goToPreviousField} field={field} value={value.answer.value as string} onChange={onFieldChange as ChangeHandler<any>} form={form} />
297
+ <HiddenValue groupFields={groupFields} isSinglePage={isSinglePage} goToNextField={goToNextField} goToPreviousField={goToPreviousField} field={field} value={value.answer.value as string} onChange={onFieldChange as ChangeHandler<any>} form={form} lastNavigationDirectionRef={lastNavigationDirectionRef} />
297
298
  )
298
299
  : field.type === 'Address' ? (
299
300
  <Address field={field} disabled={value.disabled} value={value.answer.value as any} onChange={onFieldChange as ChangeHandler<any>} form={form} />
@@ -428,13 +429,13 @@ export const QuestionForField = ({
428
429
  enduser={enduser} goToPreviousField={goToPreviousField} isPreviousDisabled={isPreviousDisabled} goToNextField={goToNextField}
429
430
  form={form} formResponseId={formResponseId} rootResponseId={rootResponseId} submit={submit}
430
431
  repeats={repeats} onRepeatsChange={onRepeatsChange} setCustomerId={setCustomerId}
431
- value={value} file={file}
432
+ value={value} file={file}
432
433
  onAddFile={onAddFile} onFieldChange={onFieldChange}
433
434
  responses={responses} selectedFiles={selectedFiles}
434
435
  validateField={validateField} enduserId={enduserId}
435
436
  spacing={field.options?.groupPadding}
436
437
  logicOptions={logicOptions}
437
- isInQuestionGroup
438
+ isInQuestionGroup
438
439
  groupFields={
439
440
  fields.filter(f => field.options?.subFields?.find(s => s.id === f.id))
440
441
  }
@@ -442,6 +443,7 @@ export const QuestionForField = ({
442
443
  uploadingFiles={uploadingFiles} setUploadingFiles={setUploadingFiles}
443
444
  handleFileUpload={handleFileUpload}
444
445
  AddToDatabase={AddToDatabase}
446
+ lastNavigationDirectionRef={lastNavigationDirectionRef}
445
447
  />
446
448
  </Flex>
447
449
  )
@@ -529,6 +531,7 @@ export const TellescopeSingleQuestionFlow: typeof TellescopeForm = ({
529
531
  groupInstance,
530
532
  logicOptions,
531
533
  uploadingFiles, setUploadingFiles, handleFileUpload,
534
+ lastNavigationDirectionRef,
532
535
  }) => {
533
536
  const beforeunloadHandler = React.useCallback((e: BeforeUnloadEvent) => {
534
537
  try {
@@ -652,6 +655,7 @@ export const TellescopeSingleQuestionFlow: typeof TellescopeForm = ({
652
655
  logicOptions={logicOptions}
653
656
  uploadingFiles={uploadingFiles} setUploadingFiles={setUploadingFiles}
654
657
  handleFileUpload={handleFileUpload}
658
+ lastNavigationDirectionRef={lastNavigationDirectionRef}
655
659
  />
656
660
  </Flex>
657
661
  </Flex>
@@ -1295,7 +1299,8 @@ export const TellescopeSinglePageForm: React.JSXElementConstructor<TellescopeFor
1295
1299
  groupInstance,
1296
1300
  uploadingFiles, setUploadingFiles, handleFileUpload,
1297
1301
  AddToDatabase,
1298
- ...props
1302
+ lastNavigationDirectionRef,
1303
+ ...props
1299
1304
  }) => {
1300
1305
  const list = useListForFormFields(fields, responses, { form: props.form, gender: enduser?.gender })
1301
1306
 
@@ -1424,7 +1429,7 @@ export const TellescopeSinglePageForm: React.JSXElementConstructor<TellescopeFor
1424
1429
  enduserId={props.enduserId} formResponseId={props.formResponseId} rootResponseId={rootResponseId} submit={submit}
1425
1430
  enduser={enduser} goToPreviousField={goToPreviousField} isPreviousDisabled={isPreviousDisabled} goToNextField={goToNextField}
1426
1431
  repeats={repeats} onRepeatsChange={setRepeats} setCustomerId={setCustomerId}
1427
- value={value} file={file}
1432
+ value={value} file={file}
1428
1433
  customInputs={customInputs}
1429
1434
  onAddFile={onAddFile} onFieldChange={onFieldChange}
1430
1435
  responses={responses} selectedFiles={selectedFiles}
@@ -1433,6 +1438,7 @@ export const TellescopeSinglePageForm: React.JSXElementConstructor<TellescopeFor
1433
1438
  uploadingFiles={uploadingFiles} setUploadingFiles={setUploadingFiles}
1434
1439
  handleFileUpload={handleFileUpload}
1435
1440
  AddToDatabase={AddToDatabase}
1441
+ lastNavigationDirectionRef={lastNavigationDirectionRef}
1436
1442
  />
1437
1443
  </Flex>
1438
1444
  </Flex>
@@ -154,6 +154,7 @@ export const QuestionForField = ({
154
154
  uploadingFiles, setUploadingFiles, handleFileUpload,
155
155
  groupFields,
156
156
  AddToDatabase,
157
+ lastNavigationDirectionRef,
157
158
  } : {
158
159
  spacing?: number,
159
160
  form?: Form,
@@ -172,7 +173,7 @@ export const QuestionForField = ({
172
173
  setUploadingFiles: React.Dispatch<React.SetStateAction<{ fieldId: string }[]>>,
173
174
  groupFields?: FormField[],
174
175
  AddToDatabase?: React.JSXElementConstructor<AddToDatabaseProps>,
175
- } & Pick<TellescopeFormProps, "rootResponseId" | "goToNextField" | "groupId" | "groupInstance" | "submit" | "formResponseId" | 'enduserId' | 'isPreviousDisabled' | 'goToPreviousField' | 'enduser' | 'handleDatabaseSelect' | 'onAddFile' | 'onFieldChange' | 'fields' | 'customInputs' | 'responses' | 'selectedFiles' | 'validateField'>) => {
176
+ } & Pick<TellescopeFormProps, "rootResponseId" | "goToNextField" | "groupId" | "groupInstance" | "submit" | "formResponseId" | 'enduserId' | 'isPreviousDisabled' | 'goToPreviousField' | 'enduser' | 'handleDatabaseSelect' | 'onAddFile' | 'onFieldChange' | 'fields' | 'customInputs' | 'responses' | 'selectedFiles' | 'validateField' | 'lastNavigationDirectionRef'>) => {
176
177
  const String = customInputs?.['string'] ?? StringInput
177
178
  const StringLong = customInputs?.['stringLong'] ?? StringLongInput
178
179
  const Email = customInputs?.['email'] ?? EmailInput
@@ -303,7 +304,7 @@ export const QuestionForField = ({
303
304
  <DateStringInput field={field} disabled={value.disabled} value={value.answer.value as string} onChange={onFieldChange as ChangeHandler<'string'>} form={form} />
304
305
  )
305
306
  : field.type === 'Hidden Value' ? (
306
- <HiddenValue groupFields={groupFields} isSinglePage={isSinglePage} goToNextField={goToNextField} goToPreviousField={goToPreviousField} field={field} value={value.answer.value as string} onChange={onFieldChange as ChangeHandler<any>} form={form} />
307
+ <HiddenValue groupFields={groupFields} isSinglePage={isSinglePage} goToNextField={goToNextField} goToPreviousField={goToPreviousField} field={field} value={value.answer.value as string} onChange={onFieldChange as ChangeHandler<any>} form={form} lastNavigationDirectionRef={lastNavigationDirectionRef} />
307
308
  )
308
309
  : field.type === 'Address' ? (
309
310
  <Address field={field} disabled={value.disabled} value={value.answer.value as any} onChange={onFieldChange as ChangeHandler<any>} form={form} />
@@ -438,13 +439,13 @@ export const QuestionForField = ({
438
439
  enduser={enduser} goToPreviousField={goToPreviousField} isPreviousDisabled={isPreviousDisabled} goToNextField={goToNextField}
439
440
  form={form} formResponseId={formResponseId} rootResponseId={rootResponseId} submit={submit}
440
441
  repeats={repeats} onRepeatsChange={onRepeatsChange} setCustomerId={setCustomerId}
441
- value={value} file={file}
442
+ value={value} file={file}
442
443
  onAddFile={onAddFile} onFieldChange={onFieldChange}
443
444
  responses={responses} selectedFiles={selectedFiles}
444
445
  validateField={validateField} enduserId={enduserId}
445
446
  spacing={field.options?.groupPadding}
446
447
  logicOptions={logicOptions}
447
- isInQuestionGroup
448
+ isInQuestionGroup
448
449
  groupFields={
449
450
  fields.filter(f => field.options?.subFields?.find(s => s.id === f.id))
450
451
  }
@@ -452,6 +453,7 @@ export const QuestionForField = ({
452
453
  uploadingFiles={uploadingFiles} setUploadingFiles={setUploadingFiles}
453
454
  handleFileUpload={handleFileUpload}
454
455
  AddToDatabase={AddToDatabase}
456
+ lastNavigationDirectionRef={lastNavigationDirectionRef}
455
457
  />
456
458
  </Flex>
457
459
  )
@@ -539,6 +541,7 @@ export const TellescopeSingleQuestionFlowV2: typeof TellescopeFormV2 = ({
539
541
  groupInstance,
540
542
  logicOptions,
541
543
  uploadingFiles, setUploadingFiles, handleFileUpload,
544
+ lastNavigationDirectionRef,
542
545
  }) => {
543
546
  const beforeunloadHandler = React.useCallback((e: BeforeUnloadEvent) => {
544
547
  try {
@@ -689,6 +692,7 @@ export const TellescopeSingleQuestionFlowV2: typeof TellescopeFormV2 = ({
689
692
  logicOptions={logicOptions}
690
693
  uploadingFiles={uploadingFiles} setUploadingFiles={setUploadingFiles}
691
694
  handleFileUpload={handleFileUpload}
695
+ lastNavigationDirectionRef={lastNavigationDirectionRef}
692
696
  />
693
697
  </Flex>
694
698
  </Flex>
@@ -1137,7 +1141,8 @@ export const TellescopeSinglePageForm: React.JSXElementConstructor<TellescopeFor
1137
1141
  groupInstance,
1138
1142
  uploadingFiles, setUploadingFiles, handleFileUpload,
1139
1143
  AddToDatabase,
1140
- ...props
1144
+ lastNavigationDirectionRef,
1145
+ ...props
1141
1146
  }) => {
1142
1147
  const list = useListForFormFields(fields, responses, { form: props.form, gender: enduser?.gender })
1143
1148
 
@@ -1266,7 +1271,7 @@ export const TellescopeSinglePageForm: React.JSXElementConstructor<TellescopeFor
1266
1271
  enduserId={props.enduserId} formResponseId={props.formResponseId} rootResponseId={rootResponseId} submit={submit}
1267
1272
  enduser={enduser} goToPreviousField={goToPreviousField} isPreviousDisabled={isPreviousDisabled} goToNextField={goToNextField}
1268
1273
  repeats={repeats} onRepeatsChange={setRepeats} setCustomerId={setCustomerId}
1269
- value={value} file={file}
1274
+ value={value} file={file}
1270
1275
  customInputs={customInputs}
1271
1276
  onAddFile={onAddFile} onFieldChange={onFieldChange}
1272
1277
  responses={responses} selectedFiles={selectedFiles}
@@ -1275,6 +1280,7 @@ export const TellescopeSinglePageForm: React.JSXElementConstructor<TellescopeFor
1275
1280
  uploadingFiles={uploadingFiles} setUploadingFiles={setUploadingFiles}
1276
1281
  handleFileUpload={handleFileUpload}
1277
1282
  AddToDatabase={AddToDatabase}
1283
+ lastNavigationDirectionRef={lastNavigationDirectionRef}
1278
1284
  />
1279
1285
  </Flex>
1280
1286
  </Flex>
@@ -501,6 +501,41 @@ const existing_response_if_compatible = (existingResponses: FormResponseValue[]
501
501
  return undefined // no valid match, write off as data loss due to incompatible type of new question
502
502
  }
503
503
 
504
+ // Filter stale multiple-choice/dropdown selections whose options are now hidden by showCondition
505
+ const filter_stale_choices = (
506
+ value: string[] | undefined,
507
+ field: FormField,
508
+ responseContext: FormResponseValue[],
509
+ enduser?: Partial<Enduser>,
510
+ form?: Form,
511
+ ): string[] | undefined => {
512
+ if (!value || !Array.isArray(value)) return value
513
+ if (field.type !== 'multiple_choice' && field.type !== 'Dropdown') return value
514
+
515
+ const options = field.options as { choices?: string[], optionDetails?: FormFieldOptionDetails[] } | undefined
516
+ if (!options?.optionDetails?.length) return value // no option-level conditions to check
517
+
518
+ const choices = options.choices ?? []
519
+
520
+ return value.filter(v => {
521
+ // preserve values not in the choices array (e.g. "other" free-text)
522
+ if (!choices.includes(v)) return true
523
+
524
+ const optionDetail = options.optionDetails?.find(d => d.option === v)
525
+ if (!optionDetail?.showCondition || object_is_empty(optionDetail.showCondition)) {
526
+ return true // no condition means always visible
527
+ }
528
+
529
+ return responses_satisfy_conditions(responseContext, optionDetail.showCondition, {
530
+ dateOfBirth: enduser?.dateOfBirth,
531
+ gender: enduser?.gender,
532
+ state: enduser?.state,
533
+ form,
534
+ activeResponses: responseContext,
535
+ })
536
+ })
537
+ }
538
+
504
539
  const shouldCallout = (field: FormField | undefined, value: FormResponseValueAnswer['value']) => {
505
540
  if (!field) return false
506
541
  if (!value) return false
@@ -546,6 +581,7 @@ export const useTellescopeForm = ({ dontAutoadvance, isPublicForm, form, urlLogi
546
581
  const [currentPageIndex, setCurrentPageIndex] = useState(0)
547
582
  const [uploadingFiles, setUploadingFiles] = useState<{ fieldId: string }[]>([])
548
583
  const prevFieldStackRef = useRef<typeof root[]>([])
584
+ const lastNavigationDirectionRef = useRef<'forward' | 'backward' | null>(null)
549
585
 
550
586
  // Auto-advance state for form continuation
551
587
  const [isAutoAdvancing, setIsAutoAdvancing] = useState(false)
@@ -654,14 +690,22 @@ export const useTellescopeForm = ({ dontAutoadvance, isPublicForm, form, urlLogi
654
690
  // placeholders for initial fields, reset when fields prop changes, since questions are now different (e.g. different form selected)
655
691
  const fieldInitRef = useRef('')
656
692
  const initializeFields = useCallback(() => (
657
- fields.map(f => ({
693
+ fields.map(f => {
694
+ const existingValue = existing_response_if_compatible(existingResponses, f)
695
+ const filteredValue = (
696
+ existingValue != null && Array.isArray(existingValue) && (f.type === 'multiple_choice' || f.type === 'Dropdown')
697
+ ? filter_stale_choices(existingValue as string[], f, existingResponses ?? [], enduser, form)
698
+ : existingValue
699
+ )
700
+
701
+ return {
658
702
  fieldId: f.id,
659
703
  fieldTitle: f.title,
660
704
  fieldDescription: f.description,
661
705
  fieldHtmlDescription: f.htmlDescription,
662
706
  externalId: f.externalId,
663
707
  intakeField: f.intakeField || undefined,
664
- touched: false,
708
+ touched: false,
665
709
  includeInSubmit: false,
666
710
  sharedWithEnduser: f.sharedWithEnduser,
667
711
  isCalledOut: existingResponses?.find(r => r.fieldId === f.id)?.isCalledOut,
@@ -681,11 +725,11 @@ export const useTellescopeForm = ({ dontAutoadvance, isPublicForm, form, urlLogi
681
725
  : f?.intakeField === 'Address' && existingResponses?.find(r => r.fieldId === f.id && r.answer.type === 'Address')
682
726
  ? 'State'
683
727
  : undefined
684
- ) as any,
685
- answer: {
728
+ ) as any,
729
+ answer: {
686
730
  type: f.type,
687
731
  value: (
688
- existing_response_if_compatible(existingResponses, f) ?? (
732
+ filteredValue ?? (
689
733
  (f.type === 'Insurance' || f.type === 'Address' || f.type === 'file' || f.type === 'signature' || f.type === 'multiple_choice' || f.type === 'Dropdown' || f.type === 'Table Input' || f.type === 'Database Select' || f.type === 'Medications' || f.type === 'Pharmacy Search')
690
734
  ? undefined
691
735
  : f.type === 'Question Group'
@@ -711,8 +755,8 @@ export const useTellescopeForm = ({ dontAutoadvance, isPublicForm, form, urlLogi
711
755
  ),
712
756
  } as FormResponseValueAnswer,
713
757
  field: f,
714
- }))
715
- ), [fields, existingResponses])
758
+ }})
759
+ ), [fields, existingResponses, enduser, form])
716
760
 
717
761
  const [responses, setResponses] = useState<(Response)[]>(initializeFields())
718
762
  useEffect(() => {
@@ -1447,12 +1491,14 @@ export const useTellescopeForm = ({ dontAutoadvance, isPublicForm, form, urlLogi
1447
1491
  responses: [
1448
1492
  ...responsesToSubmit,
1449
1493
  // include existing responses in case previously saved as draft
1450
- ...(existingResponses ?? []).filter(r =>
1494
+ ...(existingResponses ?? []).filter(r =>
1451
1495
  !responsesToSubmit.find(_r => r.fieldId === _r.fieldId)
1452
1496
  // but don't include responses which were populated from a patient field and not a prior response
1453
1497
  // if these are edited, they would be included in responsesToSubmit
1454
1498
  && !r.isPrepopulatedFromEnduserField
1455
- ),
1499
+ )
1500
+ // initializeFields leverages filter_stale_choices to strip answers whose options are no longer visible in multiple choice type questions
1501
+ // existingResponses may still carry stale values for fields the user didn't interact with this session, but preserving them as-is avoids unexpected data loss
1456
1502
  ],
1457
1503
  automationStepId,
1458
1504
  customerId,
@@ -1549,6 +1595,7 @@ export const useTellescopeForm = ({ dontAutoadvance, isPublicForm, form, urlLogi
1549
1595
  if (!currentValue) return
1550
1596
  if (isNextDisabled() && currentValue?.answer.type !== 'Hidden Value') return
1551
1597
 
1598
+ lastNavigationDirectionRef.current = 'forward'
1552
1599
  console.log('going to next field')
1553
1600
  if (currentValue.answer.type === 'Question Group') {
1554
1601
  const responsesToSave = (
@@ -1613,6 +1660,7 @@ export const useTellescopeForm = ({ dontAutoadvance, isPublicForm, form, urlLogi
1613
1660
  const goToPreviousField = useCallback(() => {
1614
1661
  if (isPreviousDisabled()) return
1615
1662
 
1663
+ lastNavigationDirectionRef.current = 'backward'
1616
1664
  updateInclusion(false)
1617
1665
 
1618
1666
  const previous = prevFieldStackRef.current.pop()
@@ -1749,6 +1797,7 @@ export const useTellescopeForm = ({ dontAutoadvance, isPublicForm, form, urlLogi
1749
1797
  uploadingFiles, setUploadingFiles,
1750
1798
  handleFileUpload,
1751
1799
  isAutoAdvancing,
1800
+ lastNavigationDirectionRef,
1752
1801
  }
1753
1802
  }
1754
1803
 
@@ -4895,7 +4895,7 @@ export const RedirectInput = ({ enduserId, groupId, groupInsance, rootResponseId
4895
4895
  return null
4896
4896
  }
4897
4897
 
4898
- export const HiddenValueInput = ({ goToNextField, goToPreviousField, field, value, onChange, isSinglePage, groupFields }: FormInputProps<'email'>) => {
4898
+ export const HiddenValueInput = ({ goToNextField, goToPreviousField, field, value, onChange, isSinglePage, groupFields, lastNavigationDirectionRef }: FormInputProps<'email'>) => {
4899
4899
  let lastRef = useRef(0)
4900
4900
  let lastIdRef = useRef('')
4901
4901
 
@@ -4925,7 +4925,10 @@ export const HiddenValueInput = ({ goToNextField, goToPreviousField, field, valu
4925
4925
  lastRef.current = Date.now()
4926
4926
  lastIdRef.current = field.id
4927
4927
 
4928
- if (value) {
4928
+ // Only collapse backward through a chain of hidden fields when actually navigating backward.
4929
+ // On forward nav (including form resume where existingResponses pre-populate the value),
4930
+ // fall through to the else branch so we re-set and advance instead of bouncing back.
4931
+ if (value && lastNavigationDirectionRef?.current === 'backward') {
4929
4932
  if (isSinglePage) return
4930
4933
  onChange('', field.id)
4931
4934
 
@@ -4939,7 +4942,7 @@ export const HiddenValueInput = ({ goToNextField, goToPreviousField, field, valu
4939
4942
  // pass value that is set after above onChange
4940
4943
  goToNextField?.({ type: 'Hidden Value', value: valueToSet })
4941
4944
  }
4942
- }, [value, onChange, field.id, valueToSet, goToNextField, goToPreviousField, isSinglePage, dontNavigate])
4945
+ }, [value, onChange, field.id, valueToSet, goToNextField, goToPreviousField, isSinglePage, dontNavigate, lastNavigationDirectionRef])
4943
4946
 
4944
4947
  return <></>
4945
4948
  }
@@ -2371,56 +2371,7 @@ export const HeightInput = ({ field, value={} as any, onChange, form, ...props }
2371
2371
  )
2372
2372
 
2373
2373
  // Re-export from V1 to follow DRY principles
2374
- export { RedirectInput } from './inputs'
2375
-
2376
- export const HiddenValueInput = ({ goToNextField, goToPreviousField, field, value, onChange, isSinglePage, groupFields }: FormInputProps<'email'>) => {
2377
- let lastRef = useRef(0)
2378
- let lastIdRef = useRef('')
2379
-
2380
- // in a Question Group, only the first Hidden Value should navigate
2381
- // AND, it should only navigate if the group only contains hidden values
2382
- const firstHiddenValue = groupFields?.find(v => v.type === 'Hidden Value')
2383
- const dontNavigate = (
2384
- (firstHiddenValue && firstHiddenValue?.id !== field.id) // is in a group, but not the first hidden value
2385
- || !!(groupFields?.find(v => v.type !== 'Hidden Value')) // group contains at least 1 non-hidden value
2386
- )
2387
-
2388
- const publicIdentifier = useMemo(() => {
2389
- try {
2390
- return new URL(window.location.href).searchParams.get('publicIdentifier') || ''
2391
- } catch(err) {
2392
- return ''
2393
- }
2394
- }, [])
2395
-
2396
- const valueToSet = useMemo(() => (
2397
- (field.title === "{{PUBLIC_IDENTIFIER}}" && publicIdentifier) ? publicIdentifier
2398
- : field.title
2399
- ), [field.title, publicIdentifier])
2400
-
2401
- useEffect(() => {
2402
- if (lastRef.current > Date.now() - 1000 && lastIdRef.current === field.id) return
2403
- lastRef.current = Date.now()
2404
- lastIdRef.current = field.id
2405
-
2406
- if (value) {
2407
- if (isSinglePage) return
2408
- onChange('', field.id)
2409
-
2410
- if (dontNavigate) return
2411
- goToPreviousField?.()
2412
- } else {
2413
- onChange(valueToSet, field.id)
2414
-
2415
- if (dontNavigate) return
2416
-
2417
- // pass value that is set after above onChange
2418
- goToNextField?.({ type: 'Hidden Value', value: valueToSet })
2419
- }
2420
- }, [value, onChange, field.id, valueToSet, goToNextField, goToPreviousField, isSinglePage, dontNavigate])
2421
-
2422
- return <></>
2423
- }
2374
+ export { RedirectInput, HiddenValueInput } from './inputs'
2424
2375
 
2425
2376
  export const EmotiiInput = ({ goToNextField, goToPreviousField, field, value, onChange, form, formResponseId, ...props }: FormInputProps<'email'>) => {
2426
2377
  const session = useResolvedSession()
@@ -39,6 +39,7 @@ export interface FormInputProps<K extends keyof AnswerForType> {
39
39
  setUploadingFiles?: React.Dispatch<React.SetStateAction<{ fieldId: string }[]>>,
40
40
  groupFields?: FormField[],
41
41
  inputProps?: { sx: SxProps },
42
+ lastNavigationDirectionRef?: React.MutableRefObject<'forward' | 'backward' | null>,
42
43
  }
43
44
 
44
45
  export type FormInputs = {