mirror of
https://github.com/formbricks/formbricks.git
synced 2026-02-21 10:08:34 -06:00
feat: optional back button (#4813)
Co-authored-by: Johannes <72809645+jobenjada@users.noreply.github.com> Co-authored-by: Anshuman Pandey <54475686+pandeymangg@users.noreply.github.com>
This commit is contained in:
committed by
GitHub
parent
b84d3d5806
commit
9f6fb8a387
@@ -61,6 +61,7 @@ export const getSurveysForEnvironmentState = reactCache(
|
||||
displayLimit: true,
|
||||
displayOption: true,
|
||||
hiddenFields: true,
|
||||
isBackButtonHidden: true,
|
||||
triggers: {
|
||||
select: {
|
||||
actionClass: {
|
||||
|
||||
@@ -7064,5 +7064,6 @@ export const previewSurvey = (projectName: string, t: TFnType) => {
|
||||
triggers: [],
|
||||
showLanguageSwitch: false,
|
||||
followUps: [],
|
||||
isBackButtonHidden: false,
|
||||
} as TSurvey;
|
||||
};
|
||||
|
||||
@@ -205,6 +205,10 @@ export const ResponseOptionsCard = ({
|
||||
}
|
||||
};
|
||||
|
||||
const handleHideBackButtonToggle = () => {
|
||||
setLocalSurvey({ ...localSurvey, isBackButtonHidden: !localSurvey.isBackButtonHidden });
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
if (!!localSurvey.surveyClosedMessage) {
|
||||
setSurveyClosedMessage({
|
||||
@@ -515,6 +519,13 @@ export const ResponseOptionsCard = ({
|
||||
</AdvancedOptionToggle>
|
||||
</>
|
||||
)}
|
||||
<AdvancedOptionToggle
|
||||
htmlId="hideBackButton"
|
||||
isChecked={localSurvey.isBackButtonHidden}
|
||||
onToggle={handleHideBackButtonToggle}
|
||||
title={t("environments.surveys.edit.hide_back_button")}
|
||||
description={t("environments.surveys.edit.hide_back_button_description")}
|
||||
/>
|
||||
</div>
|
||||
</Collapsible.CollapsibleContent>
|
||||
</Collapsible.Root>
|
||||
|
||||
@@ -41,6 +41,7 @@ export const selectSurvey = {
|
||||
pin: true,
|
||||
resultShareKey: true,
|
||||
showLanguageSwitch: true,
|
||||
isBackButtonHidden: true,
|
||||
languages: {
|
||||
select: {
|
||||
default: true,
|
||||
|
||||
@@ -41,4 +41,5 @@ export const getMinimalSurvey = (t: TFnType): TSurvey => ({
|
||||
isSingleResponsePerEmailEnabled: false,
|
||||
variables: [],
|
||||
followUps: [],
|
||||
isBackButtonHidden: false,
|
||||
});
|
||||
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 48 KiB |
@@ -47,7 +47,8 @@
|
||||
"xm-and-surveys/surveys/general-features/shareable-dashboards",
|
||||
"xm-and-surveys/surveys/general-features/schedule-start-end-dates",
|
||||
"xm-and-surveys/surveys/general-features/metadata",
|
||||
"xm-and-surveys/surveys/general-features/variables"
|
||||
"xm-and-surveys/surveys/general-features/variables",
|
||||
"xm-and-surveys/surveys/general-features/hide-back-button"
|
||||
]
|
||||
},
|
||||
{
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
---
|
||||
title: Hide Back Button
|
||||
description: Learn how to hide the back button in surveys.
|
||||
icon: arrow-left
|
||||
---
|
||||
|
||||
Surveys display a back button by default. If you want to prevent respondents from returning to previous questions, you'll need to disable this feature explicitly.
|
||||
|
||||
To disable the back button, navigate to the survey settings and select the Response options tab.
|
||||
|
||||

|
||||
@@ -0,0 +1,2 @@
|
||||
-- AlterTable
|
||||
ALTER TABLE "Response" ALTER COLUMN "updated_at" SET DEFAULT CURRENT_TIMESTAMP;
|
||||
@@ -0,0 +1,2 @@
|
||||
-- AlterTable
|
||||
ALTER TABLE "Survey" ADD COLUMN "isBackButtonHidden" BOOLEAN NOT NULL DEFAULT false;
|
||||
@@ -410,6 +410,7 @@ model Survey {
|
||||
verifyEmail Json? // deprecated
|
||||
isVerifyEmailEnabled Boolean @default(false)
|
||||
isSingleResponsePerEmailEnabled Boolean @default(false)
|
||||
isBackButtonHidden Boolean @default(false)
|
||||
pin String?
|
||||
resultShareKey String? @unique
|
||||
displayPercentage Decimal?
|
||||
|
||||
@@ -69,6 +69,7 @@ export const selectSurvey = {
|
||||
autoComplete: true,
|
||||
isVerifyEmailEnabled: true,
|
||||
isSingleResponsePerEmailEnabled: true,
|
||||
isBackButtonHidden: true,
|
||||
redirectUrl: true,
|
||||
projectOverwrites: true,
|
||||
styling: true,
|
||||
|
||||
@@ -185,6 +185,7 @@ const baseSurveyProperties = {
|
||||
displayLimit: 3,
|
||||
welcomeCard: mockWelcomeCard,
|
||||
questions: [mockQuestion],
|
||||
isBackButtonHidden: false,
|
||||
endings: [
|
||||
{
|
||||
id: "umyknohldc7w26ocjdhaa62c",
|
||||
|
||||
@@ -41,6 +41,7 @@ interface QuestionConditionalProps {
|
||||
surveyId: string;
|
||||
autoFocusEnabled: boolean;
|
||||
currentQuestionId: TSurveyQuestionId;
|
||||
isBackButtonHidden: boolean;
|
||||
}
|
||||
|
||||
export function QuestionConditional({
|
||||
@@ -60,6 +61,7 @@ export function QuestionConditional({
|
||||
onFileUpload,
|
||||
autoFocusEnabled,
|
||||
currentQuestionId,
|
||||
isBackButtonHidden,
|
||||
}: QuestionConditionalProps) {
|
||||
const getResponseValueForRankingQuestion = (
|
||||
value: string[],
|
||||
@@ -93,6 +95,7 @@ export function QuestionConditional({
|
||||
setTtc={setTtc}
|
||||
autoFocusEnabled={autoFocusEnabled}
|
||||
currentQuestionId={currentQuestionId}
|
||||
isBackButtonHidden={isBackButtonHidden}
|
||||
/>
|
||||
) : question.type === TSurveyQuestionTypeEnum.MultipleChoiceSingle ? (
|
||||
<MultipleChoiceSingleQuestion
|
||||
@@ -109,6 +112,7 @@ export function QuestionConditional({
|
||||
setTtc={setTtc}
|
||||
autoFocusEnabled={autoFocusEnabled}
|
||||
currentQuestionId={currentQuestionId}
|
||||
isBackButtonHidden={isBackButtonHidden}
|
||||
/>
|
||||
) : question.type === TSurveyQuestionTypeEnum.MultipleChoiceMulti ? (
|
||||
<MultipleChoiceMultiQuestion
|
||||
@@ -125,6 +129,7 @@ export function QuestionConditional({
|
||||
setTtc={setTtc}
|
||||
autoFocusEnabled={autoFocusEnabled}
|
||||
currentQuestionId={currentQuestionId}
|
||||
isBackButtonHidden={isBackButtonHidden}
|
||||
/>
|
||||
) : question.type === TSurveyQuestionTypeEnum.NPS ? (
|
||||
<NPSQuestion
|
||||
@@ -141,6 +146,7 @@ export function QuestionConditional({
|
||||
setTtc={setTtc}
|
||||
autoFocusEnabled={autoFocusEnabled}
|
||||
currentQuestionId={currentQuestionId}
|
||||
isBackButtonHidden={isBackButtonHidden}
|
||||
/>
|
||||
) : question.type === TSurveyQuestionTypeEnum.CTA ? (
|
||||
<CTAQuestion
|
||||
@@ -157,6 +163,7 @@ export function QuestionConditional({
|
||||
setTtc={setTtc}
|
||||
autoFocusEnabled={autoFocusEnabled}
|
||||
currentQuestionId={currentQuestionId}
|
||||
isBackButtonHidden={isBackButtonHidden}
|
||||
/>
|
||||
) : question.type === TSurveyQuestionTypeEnum.Rating ? (
|
||||
<RatingQuestion
|
||||
@@ -173,6 +180,7 @@ export function QuestionConditional({
|
||||
setTtc={setTtc}
|
||||
autoFocusEnabled={autoFocusEnabled}
|
||||
currentQuestionId={currentQuestionId}
|
||||
isBackButtonHidden={isBackButtonHidden}
|
||||
/>
|
||||
) : question.type === TSurveyQuestionTypeEnum.Consent ? (
|
||||
<ConsentQuestion
|
||||
@@ -189,6 +197,7 @@ export function QuestionConditional({
|
||||
setTtc={setTtc}
|
||||
autoFocusEnabled={autoFocusEnabled}
|
||||
currentQuestionId={currentQuestionId}
|
||||
isBackButtonHidden={isBackButtonHidden}
|
||||
/>
|
||||
) : question.type === TSurveyQuestionTypeEnum.Date ? (
|
||||
<DateQuestion
|
||||
@@ -205,6 +214,7 @@ export function QuestionConditional({
|
||||
setTtc={setTtc}
|
||||
autoFocusEnabled={autoFocusEnabled}
|
||||
currentQuestionId={currentQuestionId}
|
||||
isBackButtonHidden={isBackButtonHidden}
|
||||
/>
|
||||
) : question.type === TSurveyQuestionTypeEnum.PictureSelection ? (
|
||||
<PictureSelectionQuestion
|
||||
@@ -221,6 +231,7 @@ export function QuestionConditional({
|
||||
setTtc={setTtc}
|
||||
autoFocusEnabled={autoFocusEnabled}
|
||||
currentQuestionId={currentQuestionId}
|
||||
isBackButtonHidden={isBackButtonHidden}
|
||||
/>
|
||||
) : question.type === TSurveyQuestionTypeEnum.FileUpload ? (
|
||||
<FileUploadQuestion
|
||||
@@ -239,6 +250,7 @@ export function QuestionConditional({
|
||||
setTtc={setTtc}
|
||||
autoFocusEnabled={autoFocusEnabled}
|
||||
currentQuestionId={currentQuestionId}
|
||||
isBackButtonHidden={isBackButtonHidden}
|
||||
/>
|
||||
) : question.type === TSurveyQuestionTypeEnum.Cal ? (
|
||||
<CalQuestion
|
||||
@@ -255,6 +267,7 @@ export function QuestionConditional({
|
||||
autoFocusEnabled={autoFocusEnabled}
|
||||
setTtc={setTtc}
|
||||
currentQuestionId={currentQuestionId}
|
||||
isBackButtonHidden={isBackButtonHidden}
|
||||
/>
|
||||
) : question.type === TSurveyQuestionTypeEnum.Matrix ? (
|
||||
<MatrixQuestion
|
||||
@@ -269,6 +282,7 @@ export function QuestionConditional({
|
||||
ttc={ttc}
|
||||
setTtc={setTtc}
|
||||
currentQuestionId={currentQuestionId}
|
||||
isBackButtonHidden={isBackButtonHidden}
|
||||
/>
|
||||
) : question.type === TSurveyQuestionTypeEnum.Address ? (
|
||||
<AddressQuestion
|
||||
@@ -284,6 +298,7 @@ export function QuestionConditional({
|
||||
setTtc={setTtc}
|
||||
currentQuestionId={currentQuestionId}
|
||||
autoFocusEnabled={autoFocusEnabled}
|
||||
isBackButtonHidden={isBackButtonHidden}
|
||||
/>
|
||||
) : question.type === TSurveyQuestionTypeEnum.Ranking ? (
|
||||
<RankingQuestion
|
||||
@@ -299,6 +314,7 @@ export function QuestionConditional({
|
||||
setTtc={setTtc}
|
||||
autoFocusEnabled={autoFocusEnabled}
|
||||
currentQuestionId={currentQuestionId}
|
||||
isBackButtonHidden={isBackButtonHidden}
|
||||
/>
|
||||
) : question.type === TSurveyQuestionTypeEnum.ContactInfo ? (
|
||||
<ContactInfoQuestion
|
||||
@@ -314,6 +330,7 @@ export function QuestionConditional({
|
||||
setTtc={setTtc}
|
||||
currentQuestionId={currentQuestionId}
|
||||
autoFocusEnabled={autoFocusEnabled}
|
||||
isBackButtonHidden={isBackButtonHidden}
|
||||
/>
|
||||
) : null;
|
||||
}
|
||||
|
||||
@@ -399,6 +399,7 @@ export function Survey({
|
||||
languageCode={selectedLanguage}
|
||||
autoFocusEnabled={autoFocusEnabled}
|
||||
currentQuestionId={questionId}
|
||||
isBackButtonHidden={localSurvey.isBackButtonHidden}
|
||||
/>
|
||||
)
|
||||
);
|
||||
|
||||
@@ -25,6 +25,7 @@ interface AddressQuestionProps {
|
||||
setTtc: (ttc: TResponseTtc) => void;
|
||||
currentQuestionId: TSurveyQuestionId;
|
||||
autoFocusEnabled: boolean;
|
||||
isBackButtonHidden: boolean;
|
||||
}
|
||||
|
||||
export function AddressQuestion({
|
||||
@@ -40,6 +41,7 @@ export function AddressQuestion({
|
||||
setTtc,
|
||||
currentQuestionId,
|
||||
autoFocusEnabled,
|
||||
isBackButtonHidden,
|
||||
}: AddressQuestionProps) {
|
||||
const [startTime, setStartTime] = useState(performance.now());
|
||||
const isMediaAvailable = question.imageUrl || question.videoUrl;
|
||||
@@ -179,7 +181,7 @@ export function AddressQuestion({
|
||||
isLastQuestion={isLastQuestion}
|
||||
/>
|
||||
<div />
|
||||
{!isFirstQuestion && (
|
||||
{!isFirstQuestion && !isBackButtonHidden && (
|
||||
<BackButton
|
||||
tabIndex={isCurrent ? 0 : -1}
|
||||
backButtonLabel={getLocalizedValue(question.backButtonLabel, languageCode)}
|
||||
|
||||
@@ -24,6 +24,7 @@ interface CalQuestionProps {
|
||||
setTtc: (ttc: TResponseTtc) => void;
|
||||
autoFocusEnabled: boolean;
|
||||
currentQuestionId: TSurveyQuestionId;
|
||||
isBackButtonHidden: boolean;
|
||||
}
|
||||
|
||||
export function CalQuestion({
|
||||
@@ -38,6 +39,7 @@ export function CalQuestion({
|
||||
ttc,
|
||||
setTtc,
|
||||
currentQuestionId,
|
||||
isBackButtonHidden,
|
||||
}: CalQuestionProps) {
|
||||
const [startTime, setStartTime] = useState(performance.now());
|
||||
const isMediaAvailable = question.imageUrl || question.videoUrl;
|
||||
@@ -95,7 +97,7 @@ export function CalQuestion({
|
||||
/>
|
||||
)}
|
||||
<div />
|
||||
{!isFirstQuestion && (
|
||||
{!isFirstQuestion && !isBackButtonHidden && (
|
||||
<BackButton
|
||||
backButtonLabel={getLocalizedValue(question.backButtonLabel, languageCode)}
|
||||
onClick={() => {
|
||||
|
||||
@@ -23,6 +23,7 @@ interface ConsentQuestionProps {
|
||||
setTtc: (ttc: TResponseTtc) => void;
|
||||
autoFocusEnabled: boolean;
|
||||
currentQuestionId: TSurveyQuestionId;
|
||||
isBackButtonHidden: boolean;
|
||||
}
|
||||
|
||||
export function ConsentQuestion({
|
||||
@@ -38,6 +39,7 @@ export function ConsentQuestion({
|
||||
setTtc,
|
||||
currentQuestionId,
|
||||
autoFocusEnabled,
|
||||
isBackButtonHidden,
|
||||
}: ConsentQuestionProps) {
|
||||
const [startTime, setStartTime] = useState(performance.now());
|
||||
const isMediaAvailable = question.imageUrl || question.videoUrl;
|
||||
@@ -126,7 +128,7 @@ export function ConsentQuestion({
|
||||
isLastQuestion={isLastQuestion}
|
||||
/>
|
||||
<div />
|
||||
{!isFirstQuestion && (
|
||||
{!isFirstQuestion && !isBackButtonHidden && (
|
||||
<BackButton
|
||||
tabIndex={isCurrent ? 0 : -1}
|
||||
backButtonLabel={getLocalizedValue(question.backButtonLabel, languageCode)}
|
||||
|
||||
@@ -25,6 +25,7 @@ interface ContactInfoQuestionProps {
|
||||
setTtc: (ttc: TResponseTtc) => void;
|
||||
currentQuestionId: TSurveyQuestionId;
|
||||
autoFocusEnabled: boolean;
|
||||
isBackButtonHidden: boolean;
|
||||
}
|
||||
|
||||
export function ContactInfoQuestion({
|
||||
@@ -40,6 +41,7 @@ export function ContactInfoQuestion({
|
||||
setTtc,
|
||||
currentQuestionId,
|
||||
autoFocusEnabled,
|
||||
isBackButtonHidden,
|
||||
}: ContactInfoQuestionProps) {
|
||||
const [startTime, setStartTime] = useState(performance.now());
|
||||
const isMediaAvailable = question.imageUrl || question.videoUrl;
|
||||
@@ -181,7 +183,7 @@ export function ContactInfoQuestion({
|
||||
buttonLabel={getLocalizedValue(question.buttonLabel, languageCode)}
|
||||
isLastQuestion={isLastQuestion}
|
||||
/>
|
||||
{!isFirstQuestion && (
|
||||
{!isFirstQuestion && !isBackButtonHidden && (
|
||||
<BackButton
|
||||
tabIndex={isCurrent ? 0 : -1}
|
||||
backButtonLabel={getLocalizedValue(question.backButtonLabel, languageCode)}
|
||||
|
||||
@@ -23,6 +23,7 @@ interface CTAQuestionProps {
|
||||
setTtc: (ttc: TResponseTtc) => void;
|
||||
autoFocusEnabled: boolean;
|
||||
currentQuestionId: TSurveyQuestionId;
|
||||
isBackButtonHidden: boolean;
|
||||
}
|
||||
|
||||
export function CTAQuestion({
|
||||
@@ -37,6 +38,7 @@ export function CTAQuestion({
|
||||
setTtc,
|
||||
autoFocusEnabled,
|
||||
currentQuestionId,
|
||||
isBackButtonHidden,
|
||||
}: CTAQuestionProps) {
|
||||
const [startTime, setStartTime] = useState(performance.now());
|
||||
const isMediaAvailable = question.imageUrl || question.videoUrl;
|
||||
@@ -92,7 +94,7 @@ export function CTAQuestion({
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
{!isFirstQuestion && (
|
||||
{!isFirstQuestion && !isBackButtonHidden && (
|
||||
<BackButton
|
||||
tabIndex={isCurrent ? 0 : -1}
|
||||
backButtonLabel={getLocalizedValue(question.backButtonLabel, languageCode)}
|
||||
|
||||
@@ -28,6 +28,7 @@ interface DateQuestionProps {
|
||||
setTtc: (ttc: TResponseTtc) => void;
|
||||
autoFocusEnabled: boolean;
|
||||
currentQuestionId: TSurveyQuestionId;
|
||||
isBackButtonHidden: boolean;
|
||||
}
|
||||
|
||||
function CalendarIcon() {
|
||||
@@ -91,6 +92,7 @@ export function DateQuestion({
|
||||
setTtc,
|
||||
ttc,
|
||||
currentQuestionId,
|
||||
isBackButtonHidden,
|
||||
}: DateQuestionProps) {
|
||||
const [startTime, setStartTime] = useState(performance.now());
|
||||
const [errorMessage, setErrorMessage] = useState("");
|
||||
@@ -272,7 +274,7 @@ export function DateQuestion({
|
||||
isLastQuestion={isLastQuestion}
|
||||
buttonLabel={getLocalizedValue(question.buttonLabel, languageCode)}
|
||||
/>
|
||||
{!isFirstQuestion && (
|
||||
{!isFirstQuestion && !isBackButtonHidden && (
|
||||
<BackButton
|
||||
tabIndex={isCurrent ? 0 : -1}
|
||||
backButtonLabel={getLocalizedValue(question.backButtonLabel, languageCode)}
|
||||
|
||||
@@ -28,6 +28,7 @@ interface FileUploadQuestionProps {
|
||||
setTtc: (ttc: TResponseTtc) => void;
|
||||
autoFocusEnabled: boolean;
|
||||
currentQuestionId: TSurveyQuestionId;
|
||||
isBackButtonHidden: boolean;
|
||||
}
|
||||
|
||||
export function FileUploadQuestion({
|
||||
@@ -44,6 +45,7 @@ export function FileUploadQuestion({
|
||||
ttc,
|
||||
setTtc,
|
||||
currentQuestionId,
|
||||
isBackButtonHidden,
|
||||
}: FileUploadQuestionProps) {
|
||||
const [startTime, setStartTime] = useState(performance.now());
|
||||
const isMediaAvailable = question.imageUrl || question.videoUrl;
|
||||
@@ -110,7 +112,7 @@ export function FileUploadQuestion({
|
||||
buttonLabel={getLocalizedValue(question.buttonLabel, languageCode)}
|
||||
isLastQuestion={isLastQuestion}
|
||||
/>
|
||||
{!isFirstQuestion && (
|
||||
{!isFirstQuestion && !isBackButtonHidden && (
|
||||
<BackButton
|
||||
tabIndex={isCurrent ? 0 : -1}
|
||||
backButtonLabel={getLocalizedValue(question.backButtonLabel, languageCode)}
|
||||
|
||||
@@ -24,6 +24,7 @@ interface MatrixQuestionProps {
|
||||
ttc: TResponseTtc;
|
||||
setTtc: (ttc: TResponseTtc) => void;
|
||||
currentQuestionId: TSurveyQuestionId;
|
||||
isBackButtonHidden: boolean;
|
||||
}
|
||||
|
||||
export function MatrixQuestion({
|
||||
@@ -38,6 +39,7 @@ export function MatrixQuestion({
|
||||
ttc,
|
||||
setTtc,
|
||||
currentQuestionId,
|
||||
isBackButtonHidden,
|
||||
}: MatrixQuestionProps) {
|
||||
const [startTime, setStartTime] = useState(performance.now());
|
||||
const isMediaAvailable = question.imageUrl || question.videoUrl;
|
||||
@@ -210,7 +212,7 @@ export function MatrixQuestion({
|
||||
isLastQuestion={isLastQuestion}
|
||||
tabIndex={isCurrent ? 0 : -1}
|
||||
/>
|
||||
{!isFirstQuestion && (
|
||||
{!isFirstQuestion && !isBackButtonHidden && (
|
||||
<BackButton
|
||||
backButtonLabel={getLocalizedValue(question.backButtonLabel, languageCode)}
|
||||
onClick={handleBackButtonClick}
|
||||
|
||||
@@ -24,6 +24,7 @@ interface MultipleChoiceMultiProps {
|
||||
setTtc: (ttc: TResponseTtc) => void;
|
||||
autoFocusEnabled: boolean;
|
||||
currentQuestionId: TSurveyQuestionId;
|
||||
isBackButtonHidden: boolean;
|
||||
}
|
||||
|
||||
export function MultipleChoiceMultiQuestion({
|
||||
@@ -39,6 +40,7 @@ export function MultipleChoiceMultiQuestion({
|
||||
setTtc,
|
||||
autoFocusEnabled,
|
||||
currentQuestionId,
|
||||
isBackButtonHidden,
|
||||
}: MultipleChoiceMultiProps) {
|
||||
const [startTime, setStartTime] = useState(performance.now());
|
||||
const isMediaAvailable = question.imageUrl || question.videoUrl;
|
||||
@@ -293,7 +295,7 @@ export function MultipleChoiceMultiQuestion({
|
||||
buttonLabel={getLocalizedValue(question.buttonLabel, languageCode)}
|
||||
isLastQuestion={isLastQuestion}
|
||||
/>
|
||||
{!isFirstQuestion && (
|
||||
{!isFirstQuestion && !isBackButtonHidden && (
|
||||
<BackButton
|
||||
tabIndex={isCurrent ? 0 : -1}
|
||||
backButtonLabel={getLocalizedValue(question.backButtonLabel, languageCode)}
|
||||
|
||||
@@ -24,6 +24,7 @@ interface MultipleChoiceSingleProps {
|
||||
setTtc: (ttc: TResponseTtc) => void;
|
||||
autoFocusEnabled: boolean;
|
||||
currentQuestionId: TSurveyQuestionId;
|
||||
isBackButtonHidden: boolean;
|
||||
}
|
||||
|
||||
export function MultipleChoiceSingleQuestion({
|
||||
@@ -39,6 +40,7 @@ export function MultipleChoiceSingleQuestion({
|
||||
setTtc,
|
||||
autoFocusEnabled,
|
||||
currentQuestionId,
|
||||
isBackButtonHidden,
|
||||
}: MultipleChoiceSingleProps) {
|
||||
const [startTime, setStartTime] = useState(performance.now());
|
||||
const [otherSelected, setOtherSelected] = useState(false);
|
||||
@@ -250,7 +252,7 @@ export function MultipleChoiceSingleQuestion({
|
||||
buttonLabel={getLocalizedValue(question.buttonLabel, languageCode)}
|
||||
isLastQuestion={isLastQuestion}
|
||||
/>
|
||||
{!isFirstQuestion && (
|
||||
{!isFirstQuestion && !isBackButtonHidden && (
|
||||
<BackButton
|
||||
backButtonLabel={getLocalizedValue(question.backButtonLabel, languageCode)}
|
||||
tabIndex={isCurrent ? 0 : -1}
|
||||
|
||||
@@ -24,6 +24,7 @@ interface NPSQuestionProps {
|
||||
setTtc: (ttc: TResponseTtc) => void;
|
||||
autoFocusEnabled: boolean;
|
||||
currentQuestionId: TSurveyQuestionId;
|
||||
isBackButtonHidden: boolean;
|
||||
}
|
||||
|
||||
export function NPSQuestion({
|
||||
@@ -38,6 +39,7 @@ export function NPSQuestion({
|
||||
ttc,
|
||||
setTtc,
|
||||
currentQuestionId,
|
||||
isBackButtonHidden,
|
||||
}: NPSQuestionProps) {
|
||||
const [startTime, setStartTime] = useState(performance.now());
|
||||
const [hoveredNumber, setHoveredNumber] = useState(-1);
|
||||
@@ -153,14 +155,16 @@ export function NPSQuestion({
|
||||
</div>
|
||||
</ScrollableContainer>
|
||||
<div className="fb-flex fb-flex-row-reverse fb-w-full fb-justify-between fb-px-6 fb-py-4">
|
||||
{!question.required && (
|
||||
{question.required ? (
|
||||
<></>
|
||||
) : (
|
||||
<SubmitButton
|
||||
tabIndex={isCurrent ? 0 : -1}
|
||||
buttonLabel={getLocalizedValue(question.buttonLabel, languageCode)}
|
||||
isLastQuestion={isLastQuestion}
|
||||
/>
|
||||
)}
|
||||
{!isFirstQuestion && (
|
||||
{!isFirstQuestion && !isBackButtonHidden && (
|
||||
<BackButton
|
||||
tabIndex={isCurrent ? 0 : -1}
|
||||
backButtonLabel={getLocalizedValue(question.backButtonLabel, languageCode)}
|
||||
|
||||
@@ -25,6 +25,7 @@ interface OpenTextQuestionProps {
|
||||
setTtc: (ttc: TResponseTtc) => void;
|
||||
autoFocusEnabled: boolean;
|
||||
currentQuestionId: TSurveyQuestionId;
|
||||
isBackButtonHidden: boolean;
|
||||
}
|
||||
|
||||
export function OpenTextQuestion({
|
||||
@@ -40,6 +41,7 @@ export function OpenTextQuestion({
|
||||
setTtc,
|
||||
autoFocusEnabled,
|
||||
currentQuestionId,
|
||||
isBackButtonHidden,
|
||||
}: OpenTextQuestionProps) {
|
||||
const [startTime, setStartTime] = useState(performance.now());
|
||||
const [currentLength, setCurrentLength] = useState(value.length || 0);
|
||||
@@ -161,7 +163,7 @@ export function OpenTextQuestion({
|
||||
isLastQuestion={isLastQuestion}
|
||||
onClick={() => {}}
|
||||
/>
|
||||
{!isFirstQuestion && (
|
||||
{!isFirstQuestion && !isBackButtonHidden && (
|
||||
<BackButton
|
||||
tabIndex={isCurrent ? 0 : -1}
|
||||
backButtonLabel={getLocalizedValue(question.backButtonLabel, languageCode)}
|
||||
|
||||
@@ -25,6 +25,7 @@ interface PictureSelectionProps {
|
||||
setTtc: (ttc: TResponseTtc) => void;
|
||||
autoFocusEnabled: boolean;
|
||||
currentQuestionId: TSurveyQuestionId;
|
||||
isBackButtonHidden: boolean;
|
||||
}
|
||||
|
||||
export function PictureSelectionQuestion({
|
||||
@@ -39,6 +40,7 @@ export function PictureSelectionQuestion({
|
||||
ttc,
|
||||
setTtc,
|
||||
currentQuestionId,
|
||||
isBackButtonHidden,
|
||||
}: PictureSelectionProps) {
|
||||
const [startTime, setStartTime] = useState(performance.now());
|
||||
const isMediaAvailable = question.imageUrl || question.videoUrl;
|
||||
@@ -209,7 +211,7 @@ export function PictureSelectionQuestion({
|
||||
buttonLabel={getLocalizedValue(question.buttonLabel, languageCode)}
|
||||
isLastQuestion={isLastQuestion}
|
||||
/>
|
||||
{!isFirstQuestion && (
|
||||
{!isFirstQuestion && !isBackButtonHidden && (
|
||||
<BackButton
|
||||
tabIndex={isCurrent ? 0 : -1}
|
||||
backButtonLabel={getLocalizedValue(question.backButtonLabel, languageCode)}
|
||||
|
||||
@@ -29,6 +29,7 @@ interface RankingQuestionProps {
|
||||
setTtc: (ttc: TResponseTtc) => void;
|
||||
autoFocusEnabled: boolean;
|
||||
currentQuestionId: TSurveyQuestionId;
|
||||
isBackButtonHidden: boolean;
|
||||
}
|
||||
|
||||
export function RankingQuestion({
|
||||
@@ -44,6 +45,7 @@ export function RankingQuestion({
|
||||
setTtc,
|
||||
autoFocusEnabled,
|
||||
currentQuestionId,
|
||||
isBackButtonHidden,
|
||||
}: RankingQuestionProps) {
|
||||
const [startTime, setStartTime] = useState(performance.now());
|
||||
const isCurrent = question.id === currentQuestionId;
|
||||
@@ -272,7 +274,7 @@ export function RankingQuestion({
|
||||
buttonLabel={getLocalizedValue(question.buttonLabel, languageCode)}
|
||||
isLastQuestion={isLastQuestion}
|
||||
/>
|
||||
{!isFirstQuestion && (
|
||||
{!isFirstQuestion && !isBackButtonHidden && (
|
||||
<BackButton
|
||||
backButtonLabel={getLocalizedValue(question.backButtonLabel, languageCode)}
|
||||
tabIndex={isCurrent ? 0 : -1}
|
||||
|
||||
@@ -37,6 +37,7 @@ interface RatingQuestionProps {
|
||||
setTtc: (ttc: TResponseTtc) => void;
|
||||
autoFocusEnabled: boolean;
|
||||
currentQuestionId: TSurveyQuestionId;
|
||||
isBackButtonHidden: boolean;
|
||||
}
|
||||
|
||||
export function RatingQuestion({
|
||||
@@ -51,6 +52,7 @@ export function RatingQuestion({
|
||||
ttc,
|
||||
setTtc,
|
||||
currentQuestionId,
|
||||
isBackButtonHidden,
|
||||
}: RatingQuestionProps) {
|
||||
const [hoveredNumber, setHoveredNumber] = useState(0);
|
||||
const [startTime, setStartTime] = useState(performance.now());
|
||||
@@ -259,7 +261,9 @@ export function RatingQuestion({
|
||||
</div>
|
||||
</ScrollableContainer>
|
||||
<div className="fb-flex fb-flex-row-reverse fb-w-full fb-justify-between fb-px-6 fb-py-4">
|
||||
{!question.required && (
|
||||
{question.required ? (
|
||||
<></>
|
||||
) : (
|
||||
<SubmitButton
|
||||
tabIndex={isCurrent ? 0 : -1}
|
||||
buttonLabel={getLocalizedValue(question.buttonLabel, languageCode)}
|
||||
@@ -267,7 +271,7 @@ export function RatingQuestion({
|
||||
/>
|
||||
)}
|
||||
<div />
|
||||
{!isFirstQuestion && (
|
||||
{!isFirstQuestion && !isBackButtonHidden && (
|
||||
<BackButton
|
||||
tabIndex={isCurrent ? 0 : -1}
|
||||
backButtonLabel={getLocalizedValue(question.backButtonLabel, languageCode)}
|
||||
|
||||
@@ -35,6 +35,7 @@ export const ZJsEnvironmentStateSurvey = ZSurvey.innerType()
|
||||
displayPercentage: true,
|
||||
delay: true,
|
||||
projectOverwrites: true,
|
||||
isBackButtonHidden: true,
|
||||
})
|
||||
.superRefine(ZSurvey._def.effect.type === "refinement" ? ZSurvey._def.effect.refinement : () => null);
|
||||
|
||||
|
||||
@@ -868,13 +868,14 @@ export const ZSurvey = z
|
||||
singleUse: ZSurveySingleUse.nullable(),
|
||||
isVerifyEmailEnabled: z.boolean(),
|
||||
isSingleResponsePerEmailEnabled: z.boolean(),
|
||||
isBackButtonHidden: z.boolean(),
|
||||
pin: z.string().min(4, { message: "PIN must be a four digit number" }).nullish(),
|
||||
resultShareKey: z.string().nullable(),
|
||||
displayPercentage: z.number().min(0.01).max(100).nullable(),
|
||||
languages: z.array(ZSurveyLanguage),
|
||||
})
|
||||
.superRefine((survey, ctx) => {
|
||||
const { questions, languages, welcomeCard, endings } = survey;
|
||||
const { questions, languages, welcomeCard, endings, isBackButtonHidden } = survey;
|
||||
|
||||
let multiLangIssue: z.IssueData | null;
|
||||
|
||||
@@ -943,7 +944,9 @@ export const ZSurvey = z
|
||||
];
|
||||
|
||||
const fieldsToValidate =
|
||||
questionIndex === 0 ? initialFieldsToValidate : [...initialFieldsToValidate, "backButtonLabel"];
|
||||
questionIndex === 0 || isBackButtonHidden
|
||||
? initialFieldsToValidate
|
||||
: [...initialFieldsToValidate, "backButtonLabel"];
|
||||
|
||||
for (const field of fieldsToValidate) {
|
||||
// Skip label validation for consent questions as its called checkbox label
|
||||
|
||||
Reference in New Issue
Block a user