@tellescope/react-components 1.251.0 → 1.252.1
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/lib/cjs/CMS/ContentViewer.d.ts.map +1 -1
- package/lib/cjs/CMS/ContentViewer.js +5 -5
- package/lib/cjs/CMS/ContentViewer.js.map +1 -1
- package/lib/cjs/Community/community.js +1 -1
- package/lib/cjs/Forms/form_responses.js +3 -3
- package/lib/cjs/Forms/form_responses.js.map +1 -1
- package/lib/cjs/Forms/forms.d.ts +7 -1
- package/lib/cjs/Forms/forms.d.ts.map +1 -1
- package/lib/cjs/Forms/forms.js +18 -14
- package/lib/cjs/Forms/forms.js.map +1 -1
- package/lib/cjs/Forms/forms.v2.js +2 -2
- package/lib/cjs/Forms/hooks.d.ts.map +1 -1
- package/lib/cjs/Forms/hooks.js +22 -20
- package/lib/cjs/Forms/hooks.js.map +1 -1
- package/lib/cjs/Forms/inputs.d.ts.map +1 -1
- package/lib/cjs/Forms/inputs.js +7 -1
- package/lib/cjs/Forms/inputs.js.map +1 -1
- package/lib/cjs/displays.js +1 -1
- package/lib/cjs/displays.js.map +1 -1
- package/lib/cjs/hooks.d.ts +2 -1
- package/lib/cjs/hooks.d.ts.map +1 -1
- package/lib/cjs/hooks.js +3 -3
- package/lib/cjs/hooks.js.map +1 -1
- package/lib/esm/CMS/ContentViewer.d.ts.map +1 -1
- package/lib/esm/CMS/ContentViewer.js +6 -6
- package/lib/esm/CMS/ContentViewer.js.map +1 -1
- package/lib/esm/Community/community.js +2 -2
- package/lib/esm/Forms/form_responses.js +4 -4
- package/lib/esm/Forms/form_responses.js.map +1 -1
- package/lib/esm/Forms/forms.d.ts +7 -1
- package/lib/esm/Forms/forms.d.ts.map +1 -1
- package/lib/esm/Forms/forms.js +18 -15
- package/lib/esm/Forms/forms.js.map +1 -1
- package/lib/esm/Forms/forms.v2.js +3 -3
- package/lib/esm/Forms/hooks.d.ts.map +1 -1
- package/lib/esm/Forms/hooks.js +22 -20
- package/lib/esm/Forms/hooks.js.map +1 -1
- package/lib/esm/Forms/inputs.d.ts.map +1 -1
- package/lib/esm/Forms/inputs.js +7 -1
- package/lib/esm/Forms/inputs.js.map +1 -1
- package/lib/esm/displays.js +2 -2
- package/lib/esm/displays.js.map +1 -1
- package/lib/esm/hooks.d.ts +2 -1
- package/lib/esm/hooks.d.ts.map +1 -1
- package/lib/esm/hooks.js +3 -3
- package/lib/esm/hooks.js.map +1 -1
- package/lib/tsconfig.tsbuildinfo +1 -1
- package/package.json +9 -9
- package/src/CMS/ContentViewer.tsx +8 -6
- package/src/Community/community.tsx +2 -2
- package/src/Forms/form_responses.tsx +4 -4
- package/src/Forms/forms.tsx +11 -9
- package/src/Forms/forms.v2.tsx +3 -3
- package/src/Forms/hooks.tsx +3 -1
- package/src/Forms/inputs.tsx +6 -1
- package/src/displays.tsx +2 -2
- package/src/hooks.ts +4 -3
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tellescope/react-components",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.252.1",
|
|
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.
|
|
55
|
-
"@tellescope/sdk": "1.
|
|
56
|
-
"@tellescope/types-client": "1.
|
|
57
|
-
"@tellescope/types-models": "1.
|
|
58
|
-
"@tellescope/types-utilities": "1.
|
|
59
|
-
"@tellescope/utilities": "1.
|
|
60
|
-
"@tellescope/validation": "1.
|
|
54
|
+
"@tellescope/constants": "1.252.1",
|
|
55
|
+
"@tellescope/sdk": "1.252.1",
|
|
56
|
+
"@tellescope/types-client": "1.252.1",
|
|
57
|
+
"@tellescope/types-models": "1.252.1",
|
|
58
|
+
"@tellescope/types-utilities": "1.252.1",
|
|
59
|
+
"@tellescope/utilities": "1.252.1",
|
|
60
|
+
"@tellescope/validation": "1.252.1",
|
|
61
61
|
"@twilio/video-processors": "3.2.0",
|
|
62
62
|
"css-to-react-native": "3.0.0",
|
|
63
63
|
"draft-js": "0.11.7",
|
|
@@ -85,7 +85,7 @@
|
|
|
85
85
|
"react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0",
|
|
86
86
|
"react-native": "^0.65.0 || ^0.66.0 || ^0.67.0 || ^0.68.0 || ^0.71.0"
|
|
87
87
|
},
|
|
88
|
-
"gitHead": "
|
|
88
|
+
"gitHead": "f5dc3564f0f61a9dc910ada9a55a68fad2217bec",
|
|
89
89
|
"publishConfig": {
|
|
90
90
|
"access": "public"
|
|
91
91
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import React, { useEffect, useLayoutEffect, useRef, useState } from "react"
|
|
2
2
|
import { ManagedContentRecord } from "@tellescope/types-client"
|
|
3
|
-
import {
|
|
3
|
+
import { sanitize_user_html, sanitize_html_for_cms } from "@tellescope/utilities"
|
|
4
4
|
import { Button, Grid, Typography } from "@mui/material"
|
|
5
5
|
import { PDFBlockUI } from "./components"
|
|
6
6
|
import { css } from "@emotion/css"
|
|
@@ -190,7 +190,7 @@ export const ArticleViewer = ({
|
|
|
190
190
|
else if (article.htmlContent) {
|
|
191
191
|
return (
|
|
192
192
|
<div style={style} dangerouslySetInnerHTML={{
|
|
193
|
-
__html:
|
|
193
|
+
__html: sanitize_user_html(article.htmlContent)
|
|
194
194
|
}} />
|
|
195
195
|
)
|
|
196
196
|
} else {
|
|
@@ -224,7 +224,7 @@ export const ArticleViewer = ({
|
|
|
224
224
|
margin-bottom: 0;
|
|
225
225
|
}`}
|
|
226
226
|
dangerouslySetInnerHTML={{
|
|
227
|
-
__html:
|
|
227
|
+
__html: sanitize_user_html(
|
|
228
228
|
block.info.html.replaceAll(/style="*"/g, '')
|
|
229
229
|
)
|
|
230
230
|
}}
|
|
@@ -253,9 +253,10 @@ export const ArticleViewer = ({
|
|
|
253
253
|
: block.type === 'youtube' ? (
|
|
254
254
|
<iframe width={rootWidth} height={rootWidth * 315 / 560}
|
|
255
255
|
title={`YouTube video player ${i}`} allowFullScreen
|
|
256
|
+
referrerPolicy="strict-origin-when-cross-origin"
|
|
256
257
|
src={correct_youtube_link_for_embed(block.info.link)}
|
|
257
258
|
>
|
|
258
|
-
</iframe>
|
|
259
|
+
</iframe>
|
|
259
260
|
)
|
|
260
261
|
: block.type === 'iframe' ? (
|
|
261
262
|
<iframe width={rootWidth}
|
|
@@ -306,7 +307,7 @@ export const html_for_article = (article: ManagedContentRecord, options?: { root
|
|
|
306
307
|
`<h2>${block.info.text}</h2>`
|
|
307
308
|
)
|
|
308
309
|
: block.type === 'html' ? (
|
|
309
|
-
`<div>${
|
|
310
|
+
`<div>${sanitize_user_html(sanitize_user_html(block.info.html))}</div>`
|
|
310
311
|
)
|
|
311
312
|
: block.type === 'raw-html' ? (
|
|
312
313
|
`<div>${sanitize_html_for_cms(block.info.html)}</div>`
|
|
@@ -318,10 +319,11 @@ export const html_for_article = (article: ManagedContentRecord, options?: { root
|
|
|
318
319
|
</div>`
|
|
319
320
|
)
|
|
320
321
|
: block.type === 'youtube' ? (
|
|
321
|
-
`<iframe width="${rootWidth}"
|
|
322
|
+
`<iframe width="${rootWidth}"
|
|
322
323
|
height="${rootWidth * 315 / 560}"
|
|
323
324
|
title="${`YouTube video player ${i}`}"
|
|
324
325
|
allowFullScreen
|
|
326
|
+
referrerpolicy="strict-origin-when-cross-origin"
|
|
325
327
|
src="${correct_youtube_link_for_embed(block.info.link)}"
|
|
326
328
|
style="margin-top: 12; margin-bottom: 12"
|
|
327
329
|
>
|
|
@@ -28,7 +28,7 @@ import { Divider, Grid, LinearProgress, Paper, TextField, Typography } from "@mu
|
|
|
28
28
|
import {
|
|
29
29
|
usePostLiking,
|
|
30
30
|
} from "./hooks"
|
|
31
|
-
import {
|
|
31
|
+
import { sanitize_user_html, truncate_string, user_is_admin } from "@tellescope/utilities"
|
|
32
32
|
|
|
33
33
|
import LikeIcon from "@mui/icons-material/ThumbUp"
|
|
34
34
|
|
|
@@ -50,7 +50,7 @@ export const ResolvedContent = ({ textContent, htmlContent, style } : { textCont
|
|
|
50
50
|
if (htmlContent) {
|
|
51
51
|
return (
|
|
52
52
|
<div style={style} dangerouslySetInnerHTML={{
|
|
53
|
-
__html:
|
|
53
|
+
__html: sanitize_user_html(htmlContent),
|
|
54
54
|
}} />
|
|
55
55
|
)
|
|
56
56
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import React, { useEffect } from "react"
|
|
2
2
|
import { Divider, Grid, Typography } from "@mui/material"
|
|
3
3
|
import { Enduser, FormResponse } from "@tellescope/types-client"
|
|
4
|
-
import { form_response_value_to_string, formatted_date, getOrgnizationLogoURL,
|
|
4
|
+
import { form_response_value_to_string, formatted_date, getOrgnizationLogoURL, sanitize_user_html, user_display_name } from "@tellescope/utilities"
|
|
5
5
|
import { DownloadFileIconButton, ImageProps, LabeledIconButton, SecureImage, useEndusers, useEnduserMedications, useEnduserObservations, useOrganization, useResolvedSession, useSession, useUsers, value_is_loaded } from "../index"
|
|
6
6
|
import CloseIcon from '@mui/icons-material/Close';
|
|
7
7
|
import { DatabaseSelectResponse, FormResponseAnswerAddress, FormResponseValueAnswer } from "@tellescope/types-models"
|
|
@@ -161,7 +161,7 @@ export const ResponseAnswer = ({ formResponse, fieldId, isHTML, answer: a, print
|
|
|
161
161
|
isHTML?: boolean,
|
|
162
162
|
}) => (
|
|
163
163
|
((isHTML || a.type === 'Rich Text') && typeof a.value === 'string')
|
|
164
|
-
? <div dangerouslySetInnerHTML={{ __html:
|
|
164
|
+
? <div dangerouslySetInnerHTML={{ __html: sanitize_user_html(a.value) }} />
|
|
165
165
|
: a.value
|
|
166
166
|
? (
|
|
167
167
|
<Typography component="div">
|
|
@@ -461,7 +461,7 @@ export const FormResponseView = ({ showAnswerInline=true, logoURL, enduser, onCl
|
|
|
461
461
|
&& !(typeof r.answer.value === 'string' && r.answer.value.includes('{TELLESCOPE')) // hidden field for matching, not to display
|
|
462
462
|
&& (
|
|
463
463
|
(r.answerIsHTML && typeof r.answer.value === 'string')
|
|
464
|
-
? <div dangerouslySetInnerHTML={{ __html:
|
|
464
|
+
? <div dangerouslySetInnerHTML={{ __html: sanitize_user_html(r.answer.value) }} />
|
|
465
465
|
: <ResponseAnswer fieldId={r.fieldId} formResponse={response} answer={r.answer} printing={printing} />
|
|
466
466
|
)
|
|
467
467
|
}
|
|
@@ -476,7 +476,7 @@ export const FormResponseView = ({ showAnswerInline=true, logoURL, enduser, onCl
|
|
|
476
476
|
): r.fieldHtmlDescription
|
|
477
477
|
? (
|
|
478
478
|
<div dangerouslySetInnerHTML={{
|
|
479
|
-
__html: r.fieldHtmlDescription
|
|
479
|
+
__html: sanitize_user_html(r.fieldHtmlDescription)
|
|
480
480
|
}} />
|
|
481
481
|
)
|
|
482
482
|
: null
|
package/src/Forms/forms.tsx
CHANGED
|
@@ -3,10 +3,10 @@ import { Button, CircularProgress, FileBlob, FileUploadHandler, Flex, LinearProg
|
|
|
3
3
|
import { useListForFormFields, useOrganizationTheme, useTellescopeForm, WithOrganizationTheme, Response, FileResponse, NextFieldLogicOptions } from "./hooks"
|
|
4
4
|
import { ChangeHandler, FormInputs } from "./types"
|
|
5
5
|
import { AddToDatabaseProps, AddressInput, AllergiesInput, AppointmentBookingInput, BelugaPatientPreferenceInput, BridgeEligibilityInput, CandidEligibilityInput, ChargeebeeInput, ConditionsInput, DatabaseSelectInput, DateInput, DateStringInput, DropdownInput, EmailInput, EmotiiInput, FileInput, FilesInput, HeightInput, HiddenValueInput, InsuranceInput, LanguageSelect, MedicationsInput, MultipleChoiceInput, NumberInput, PharmacySearchInput, PhoneInput, Progress, RankingInput, RatingInput, RedirectInput, RelatedContactsInput, RichTextInput, SignatureInput, StringInput, StringLongInput, StripeInput, TableInput, TimeInput, TimezoneInput, defaultButtonStyles } from "./inputs"
|
|
6
|
-
import { PRIMARY_HEX } from "@tellescope/constants"
|
|
6
|
+
import { PRIMARY_HEX, DEFAULT_HISTORICAL_DATA_SOURCE_LIMIT } from "@tellescope/constants"
|
|
7
7
|
import { FormResponse, FormField, Form, Enduser } from "@tellescope/types-client"
|
|
8
8
|
import { FormResponseAnswerFileValue, OrganizationTheme, HistoricalDataSource } from "@tellescope/types-models"
|
|
9
|
-
import { calculate_form_scoring, field_can_autosubmit, form_response_value_to_string, formatted_date, object_is_empty, objects_equivalent, read_local_storage,
|
|
9
|
+
import { calculate_form_scoring, field_can_autosubmit, form_response_value_to_string, formatted_date, object_is_empty, objects_equivalent, read_local_storage, sanitize_user_html, responses_satisfy_conditions, truncate_string } from "@tellescope/utilities"
|
|
10
10
|
import { Divider } from "@mui/material"
|
|
11
11
|
|
|
12
12
|
export const TellescopeFormContainer = ({ businessId, organizationIds, ...props } : {
|
|
@@ -798,7 +798,7 @@ export const ThanksMessage = ({
|
|
|
798
798
|
{htmlThanksMessage
|
|
799
799
|
? (
|
|
800
800
|
<div style={{ textAlign: 'center' }} dangerouslySetInnerHTML={{
|
|
801
|
-
__html:
|
|
801
|
+
__html: sanitize_user_html(htmlThanksMessage)
|
|
802
802
|
}} />
|
|
803
803
|
) : (
|
|
804
804
|
<Typography style={{ marginTop: 25, alignSelf: 'center' }}>{thanksMessage || DEFAULT_THANKS_MESSAGE}</Typography>
|
|
@@ -1037,7 +1037,7 @@ export const UpdateResponse = ({
|
|
|
1037
1037
|
)
|
|
1038
1038
|
}
|
|
1039
1039
|
|
|
1040
|
-
const HistoricalDataSection = ({ sources, enduserId, onDataLoaded } : { sources: HistoricalDataSource[], enduserId: string, onDataLoaded?: (json: string) => void }) => {
|
|
1040
|
+
export const HistoricalDataSection = ({ sources, enduserId, onDataLoaded, hideHeaders } : { sources: HistoricalDataSource[], enduserId: string, onDataLoaded?: (json: string) => void, hideHeaders?: boolean }) => {
|
|
1041
1041
|
const session = useSession({ throwIfMissingContext: false })
|
|
1042
1042
|
const [observations, setObservations] = useState<any[]>([])
|
|
1043
1043
|
const [medications, setMedications] = useState<any[]>([])
|
|
@@ -1067,7 +1067,9 @@ const HistoricalDataSection = ({ sources, enduserId, onDataLoaded } : { sources:
|
|
|
1067
1067
|
promises.push(
|
|
1068
1068
|
session.api.enduser_observations.getSome({
|
|
1069
1069
|
filter: { enduserId, ...source.filter },
|
|
1070
|
-
limit: source.limit,
|
|
1070
|
+
limit: source.limit ?? DEFAULT_HISTORICAL_DATA_SOURCE_LIMIT,
|
|
1071
|
+
sortBy: 'timestamp',
|
|
1072
|
+
sort: 'newFirst',
|
|
1071
1073
|
})
|
|
1072
1074
|
.then((obs: any[]) => { loadedObservations = obs; setObservations(obs) })
|
|
1073
1075
|
)
|
|
@@ -1075,7 +1077,7 @@ const HistoricalDataSection = ({ sources, enduserId, onDataLoaded } : { sources:
|
|
|
1075
1077
|
promises.push(
|
|
1076
1078
|
session.api.enduser_medications.getSome({
|
|
1077
1079
|
filter: { enduserId, status: { _ne: 'draft' }, ...source.filter },
|
|
1078
|
-
limit: source.limit,
|
|
1080
|
+
limit: source.limit ?? DEFAULT_HISTORICAL_DATA_SOURCE_LIMIT,
|
|
1079
1081
|
})
|
|
1080
1082
|
.then((meds: any[]) => { loadedMedications = meds; setMedications(meds) })
|
|
1081
1083
|
)
|
|
@@ -1141,7 +1143,7 @@ const HistoricalDataSection = ({ sources, enduserId, onDataLoaded } : { sources:
|
|
|
1141
1143
|
<div style={{ marginTop: 10 }}>
|
|
1142
1144
|
{hasObservations && (
|
|
1143
1145
|
<div style={{ marginBottom: 15 }}>
|
|
1144
|
-
<Typography style={{ fontWeight: 'bold', marginBottom: 5 }}>Observations</Typography>
|
|
1146
|
+
{!hideHeaders && <Typography style={{ fontWeight: 'bold', marginBottom: 5 }}>Observations</Typography>}
|
|
1145
1147
|
{observations.length === 0 ? (
|
|
1146
1148
|
<Typography style={{ fontStyle: 'italic', color: '#888' }}>No observations found</Typography>
|
|
1147
1149
|
) : (
|
|
@@ -1175,7 +1177,7 @@ const HistoricalDataSection = ({ sources, enduserId, onDataLoaded } : { sources:
|
|
|
1175
1177
|
|
|
1176
1178
|
{hasMedications && (
|
|
1177
1179
|
<div style={{ marginBottom: 15 }}>
|
|
1178
|
-
<Typography style={{ fontWeight: 'bold', marginBottom: 5 }}>Medications</Typography>
|
|
1180
|
+
{!hideHeaders && <Typography style={{ fontWeight: 'bold', marginBottom: 5 }}>Medications</Typography>}
|
|
1179
1181
|
{medications.length === 0 ? (
|
|
1180
1182
|
<Typography style={{ fontStyle: 'italic', color: '#888' }}>No medications found</Typography>
|
|
1181
1183
|
) : (
|
|
@@ -1236,7 +1238,7 @@ export const Description = ({ field, color="primary", style, enduserId, onFieldC
|
|
|
1236
1238
|
</Typography>
|
|
1237
1239
|
) : field.htmlDescription ? (
|
|
1238
1240
|
<span dangerouslySetInnerHTML={{
|
|
1239
|
-
__html:
|
|
1241
|
+
__html: sanitize_user_html(field.htmlDescription)
|
|
1240
1242
|
}} />
|
|
1241
1243
|
) : null
|
|
1242
1244
|
)
|
package/src/Forms/forms.v2.tsx
CHANGED
|
@@ -6,7 +6,7 @@ import { AddToDatabaseProps, AddressInput, AllergiesInput, AppointmentBookingInp
|
|
|
6
6
|
import { PRIMARY_HEX } from "@tellescope/constants"
|
|
7
7
|
import { FormResponse, FormField, Form, Enduser } from "@tellescope/types-client"
|
|
8
8
|
import { FormResponseAnswerFileValue, OrganizationTheme } from "@tellescope/types-models"
|
|
9
|
-
import { calculate_form_scoring, field_can_autosubmit, form_response_value_to_string, formatted_date, object_is_empty, objects_equivalent, read_local_storage,
|
|
9
|
+
import { calculate_form_scoring, field_can_autosubmit, form_response_value_to_string, formatted_date, object_is_empty, objects_equivalent, read_local_storage, sanitize_user_html, responses_satisfy_conditions, truncate_string } from "@tellescope/utilities"
|
|
10
10
|
import { Divider } from "@mui/material"
|
|
11
11
|
|
|
12
12
|
export const TellescopeFormContainerV2 = ({ businessId, organizationIds, ...props } : {
|
|
@@ -836,7 +836,7 @@ export const ThanksMessage = ({
|
|
|
836
836
|
{htmlThanksMessage
|
|
837
837
|
? (
|
|
838
838
|
<div style={{ textAlign: 'center' }} dangerouslySetInnerHTML={{
|
|
839
|
-
__html:
|
|
839
|
+
__html: sanitize_user_html(htmlThanksMessage)
|
|
840
840
|
}} />
|
|
841
841
|
) : (
|
|
842
842
|
<Typography style={{ marginTop: 25, alignSelf: 'center' }}>{thanksMessage || DEFAULT_THANKS_MESSAGE}</Typography>
|
|
@@ -1092,7 +1092,7 @@ export const Description = ({ field, color="primary", style } : { field: FormFie
|
|
|
1092
1092
|
|
|
1093
1093
|
return (
|
|
1094
1094
|
<span style={style} dangerouslySetInnerHTML={{
|
|
1095
|
-
__html:
|
|
1095
|
+
__html: sanitize_user_html(field.htmlDescription)
|
|
1096
1096
|
}} />
|
|
1097
1097
|
)
|
|
1098
1098
|
}
|
package/src/Forms/hooks.tsx
CHANGED
|
@@ -1219,7 +1219,9 @@ export const useTellescopeForm = ({ dontAutoadvance, isPublicForm, form, urlLogi
|
|
|
1219
1219
|
if (field.type === 'files' && value.answer.type === 'files' && Array.isArray(value.answer.value)) {
|
|
1220
1220
|
existingCount = value.answer.value.filter(av => !file.blobs?.some(b => b.name === av.name)).length
|
|
1221
1221
|
} else if (field.type === 'file' && value.answer.type === 'file' && value.answer.value?.secureName) {
|
|
1222
|
-
|
|
1222
|
+
const existingFileName = value.answer.value.name
|
|
1223
|
+
const hasMatchingBlob = file.blobs?.some(b => b.name === existingFileName)
|
|
1224
|
+
existingCount = hasMatchingBlob ? 0 : 1
|
|
1223
1225
|
}
|
|
1224
1226
|
|
|
1225
1227
|
const totalCount = blobCount + existingCount
|
package/src/Forms/inputs.tsx
CHANGED
|
@@ -5060,8 +5060,13 @@ export const HiddenValueInput = ({ goToNextField, goToPreviousField, field, valu
|
|
|
5060
5060
|
if (dontNavigate) return
|
|
5061
5061
|
goToPreviousField?.()
|
|
5062
5062
|
} else {
|
|
5063
|
-
|
|
5063
|
+
// Avoid redundant setResponses when value is already correct — prevents the effect
|
|
5064
|
+
// from cascading into every keystroke on sibling text fields in single-page forms.
|
|
5065
|
+
if (value !== valueToSet) {
|
|
5066
|
+
onChange(valueToSet, field.id)
|
|
5067
|
+
}
|
|
5064
5068
|
|
|
5069
|
+
if (isSinglePage) return
|
|
5065
5070
|
if (dontNavigate) return
|
|
5066
5071
|
|
|
5067
5072
|
// pass value that is set after above onChange
|
package/src/displays.tsx
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import React, { useEffect, useRef, useState } from "react"
|
|
2
2
|
|
|
3
|
-
import { user_display_name } from "@tellescope/utilities"
|
|
3
|
+
import { sanitize_user_html, user_display_name } from "@tellescope/utilities"
|
|
4
4
|
|
|
5
5
|
import {
|
|
6
6
|
useEndusers,
|
|
@@ -304,5 +304,5 @@ export const replace_links = (html: string) => {
|
|
|
304
304
|
html = html.replace(linkTemplate, replacementHTML)
|
|
305
305
|
}
|
|
306
306
|
|
|
307
|
-
return <span dangerouslySetInnerHTML={{ __html: html }} />
|
|
307
|
+
return <span dangerouslySetInnerHTML={{ __html: sanitize_user_html(html) }} />
|
|
308
308
|
}
|
package/src/hooks.ts
CHANGED
|
@@ -31,8 +31,9 @@ export const useLoadedState = <T, D={}>(fetch?: (d: Partial<D>) => Promise<T | v
|
|
|
31
31
|
export interface SearchAPIProps <T> {
|
|
32
32
|
searchAPI?: (args: { search: { query: string } }) => Promise<T[]>,
|
|
33
33
|
onLoad?: (results: T[]) => void,
|
|
34
|
+
debounceMS?: number,
|
|
34
35
|
}
|
|
35
|
-
export const useSearchAPI = <T,>({ query, onLoad, searchAPI } : { query: string } & SearchAPIProps<T>) => {
|
|
36
|
+
export const useSearchAPI = <T,>({ query, onLoad, searchAPI, debounceMS = 300 } : { query: string } & SearchAPIProps<T>) => {
|
|
36
37
|
const searchedRef = useRef('')
|
|
37
38
|
|
|
38
39
|
useEffect(() => {
|
|
@@ -56,10 +57,10 @@ export const useSearchAPI = <T,>({ query, onLoad, searchAPI } : { query: string
|
|
|
56
57
|
onLoad?.(results)
|
|
57
58
|
})
|
|
58
59
|
.catch(console.error)
|
|
59
|
-
},
|
|
60
|
+
}, debounceMS)
|
|
60
61
|
|
|
61
62
|
return () => { clearTimeout(t) }
|
|
62
|
-
}, [query, searchAPI, onLoad, searchedRef])
|
|
63
|
+
}, [query, searchAPI, onLoad, searchedRef, debounceMS])
|
|
63
64
|
}
|
|
64
65
|
|
|
65
66
|
export const useAddGTMTag = (gtmTag?: string) => {
|