mirror of
https://github.com/formbricks/formbricks.git
synced 2026-05-07 19:30:07 -05:00
feat: custom placeholder label for other option in single & multi select (#1971)
Co-authored-by: Johannes <72809645+jobenjada@users.noreply.github.com>
This commit is contained in:
committed by
GitHub
parent
0122ccb797
commit
16cbc3365b
+37
-22
@@ -217,29 +217,44 @@ export default function MultipleChoiceMultiForm({
|
||||
{question.choices &&
|
||||
question.choices.map((choice, choiceIdx) => (
|
||||
<div key={choiceIdx} className="inline-flex w-full items-center">
|
||||
<Input
|
||||
ref={choiceIdx === question.choices.length - 1 ? lastChoiceRef : null}
|
||||
id={choice.id}
|
||||
name={choice.id}
|
||||
value={choice.label}
|
||||
className={cn(choice.id === "other" && "border-dashed")}
|
||||
placeholder={choice.id === "other" ? "Other" : `Option ${choiceIdx + 1}`}
|
||||
onChange={(e) => updateChoice(choiceIdx, { label: e.target.value })}
|
||||
onBlur={() => {
|
||||
const duplicateLabel = findDuplicateLabel();
|
||||
if (duplicateLabel) {
|
||||
setisInvalidValue(duplicateLabel);
|
||||
} else if (findEmptyLabel()) {
|
||||
setisInvalidValue("");
|
||||
} else {
|
||||
setisInvalidValue(null);
|
||||
<div className="flex w-full space-x-2">
|
||||
<Input
|
||||
ref={choiceIdx === question.choices.length - 1 ? lastChoiceRef : null}
|
||||
id={choice.id}
|
||||
name={choice.id}
|
||||
value={choice.label}
|
||||
className={cn(choice.id === "other" && "border-dashed")}
|
||||
placeholder={choice.id === "other" ? "Other" : `Option ${choiceIdx + 1}`}
|
||||
onChange={(e) => updateChoice(choiceIdx, { label: e.target.value })}
|
||||
onBlur={() => {
|
||||
const duplicateLabel = findDuplicateLabel();
|
||||
if (duplicateLabel) {
|
||||
setisInvalidValue(duplicateLabel);
|
||||
} else if (findEmptyLabel()) {
|
||||
setisInvalidValue("");
|
||||
} else {
|
||||
setisInvalidValue(null);
|
||||
}
|
||||
}}
|
||||
isInvalid={
|
||||
(isInvalidValue === "" && choice.label.trim() === "") ||
|
||||
(isInvalidValue !== null && choice.label.trim() === isInvalidValue.trim())
|
||||
}
|
||||
}}
|
||||
isInvalid={
|
||||
(isInvalidValue === "" && choice.label.trim() === "") ||
|
||||
(isInvalidValue !== null && choice.label.trim() === isInvalidValue.trim())
|
||||
}
|
||||
/>
|
||||
/>
|
||||
{choice.id === "other" && (
|
||||
<Input
|
||||
id="otherInputLabel"
|
||||
name="otherInputLabel"
|
||||
value={question.otherOptionPlaceholder ?? "Please specify"}
|
||||
placeholder={question.otherOptionPlaceholder ?? "Please specify"}
|
||||
className={cn(choice.id === "other" && "border-dashed")}
|
||||
onChange={(e) => {
|
||||
if (e.target.value.trim() == "") e.target.value = "";
|
||||
updateQuestion(questionIdx, { otherOptionPlaceholder: e.target.value });
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
{question.choices && question.choices.length > 2 && (
|
||||
<TrashIcon
|
||||
className="ml-2 h-4 w-4 cursor-pointer text-slate-400 hover:text-slate-500"
|
||||
|
||||
+38
-23
@@ -216,30 +216,45 @@ export default function MultipleChoiceSingleForm({
|
||||
<div className="mt-2 space-y-2" id="choices">
|
||||
{question.choices &&
|
||||
question.choices.map((choice, choiceIdx) => (
|
||||
<div key={choiceIdx} className="inline-flex w-full items-center">
|
||||
<Input
|
||||
ref={choiceIdx === question.choices.length - 1 ? lastChoiceRef : null}
|
||||
id={choice.id}
|
||||
name={choice.id}
|
||||
value={choice.label}
|
||||
className={cn(choice.id === "other" && "border-dashed")}
|
||||
placeholder={choice.id === "other" ? "Other" : `Option ${choiceIdx + 1}`}
|
||||
onBlur={() => {
|
||||
const duplicateLabel = findDuplicateLabel();
|
||||
if (duplicateLabel) {
|
||||
setisInvalidValue(duplicateLabel);
|
||||
} else if (findEmptyLabel()) {
|
||||
setisInvalidValue("");
|
||||
} else {
|
||||
setisInvalidValue(null);
|
||||
<div key={choiceIdx} className="flex w-full items-center">
|
||||
<div className="flex w-full space-x-2">
|
||||
<Input
|
||||
ref={choiceIdx === question.choices.length - 1 ? lastChoiceRef : null}
|
||||
id={choice.id}
|
||||
name={choice.id}
|
||||
value={choice.label}
|
||||
className={cn(choice.id === "other" && "border-dashed")}
|
||||
placeholder={choice.id === "other" ? "Other" : `Option ${choiceIdx + 1}`}
|
||||
onBlur={() => {
|
||||
const duplicateLabel = findDuplicateLabel();
|
||||
if (duplicateLabel) {
|
||||
setisInvalidValue(duplicateLabel);
|
||||
} else if (findEmptyLabel()) {
|
||||
setisInvalidValue("");
|
||||
} else {
|
||||
setisInvalidValue(null);
|
||||
}
|
||||
}}
|
||||
onChange={(e) => updateChoice(choiceIdx, { label: e.target.value })}
|
||||
isInvalid={
|
||||
(isInvalidValue === "" && choice.label.trim() === "") ||
|
||||
(isInvalidValue !== null && choice.label.trim() === isInvalidValue.trim())
|
||||
}
|
||||
}}
|
||||
onChange={(e) => updateChoice(choiceIdx, { label: e.target.value })}
|
||||
isInvalid={
|
||||
(isInvalidValue === "" && choice.label.trim() === "") ||
|
||||
(isInvalidValue !== null && choice.label.trim() === isInvalidValue.trim())
|
||||
}
|
||||
/>
|
||||
/>
|
||||
{choice.id === "other" && (
|
||||
<Input
|
||||
id="otherInputLabel"
|
||||
name="otherInputLabel"
|
||||
value={question.otherOptionPlaceholder ?? "Please specify"}
|
||||
placeholder={question.otherOptionPlaceholder ?? "Please specify"}
|
||||
className={cn(choice.id === "other" && "border-dashed")}
|
||||
onChange={(e) => {
|
||||
if (e.target.value.trim() == "") e.target.value = "";
|
||||
updateQuestion(questionIdx, { otherOptionPlaceholder: e.target.value });
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
{question.choices && question.choices.length > 2 && (
|
||||
<TrashIcon
|
||||
className="ml-2 h-4 w-4 cursor-pointer text-slate-400 hover:text-slate-500"
|
||||
|
||||
@@ -231,7 +231,7 @@ export default function MultipleChoiceMultiQuestion({
|
||||
}, 100);
|
||||
}
|
||||
}}
|
||||
placeholder="Please specify"
|
||||
placeholder={question.otherOptionPlaceholder ?? "Please specify"}
|
||||
className="placeholder:text-placeholder border-border bg-survey-bg text-heading focus:ring-focus mt-3 flex h-10 w-full rounded-md border px-3 py-2 text-sm focus:outline-none focus:ring-2 focus:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50"
|
||||
required={question.required}
|
||||
aria-labelledby={`${otherOption.id}-label`}
|
||||
|
||||
@@ -182,7 +182,7 @@ export default function MultipleChoiceSingleQuestion({
|
||||
}, 100);
|
||||
}
|
||||
}}
|
||||
placeholder="Please specify"
|
||||
placeholder={question.otherOptionPlaceholder ?? "Please specify"}
|
||||
className="placeholder:text-placeholder border-border bg-survey-bg text-heading focus:ring-focus mt-3 flex h-10 w-full rounded-md border px-3 py-2 text-sm focus:outline-none focus:ring-2 focus:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50"
|
||||
required={question.required}
|
||||
aria-labelledby={`${otherOption.id}-label`}
|
||||
|
||||
@@ -277,6 +277,7 @@ export const ZSurveyMultipleChoiceSingleQuestion = ZSurveyQuestionBase.extend({
|
||||
choices: z.array(ZSurveyChoice),
|
||||
logic: z.array(ZSurveyMultipleChoiceSingleLogic).optional(),
|
||||
shuffleOption: z.enum(["none", "all", "exceptLast"]).optional(),
|
||||
otherOptionPlaceholder: z.string().optional(),
|
||||
});
|
||||
|
||||
export type TSurveyMultipleChoiceSingleQuestion = z.infer<typeof ZSurveyMultipleChoiceSingleQuestion>;
|
||||
@@ -286,6 +287,7 @@ export const ZSurveyMultipleChoiceMultiQuestion = ZSurveyQuestionBase.extend({
|
||||
choices: z.array(ZSurveyChoice),
|
||||
logic: z.array(ZSurveyMultipleChoiceMultiLogic).optional(),
|
||||
shuffleOption: z.enum(["none", "all", "exceptLast"]).optional(),
|
||||
otherOptionPlaceholder: z.string().optional(),
|
||||
});
|
||||
|
||||
export type TSurveyMultipleChoiceMultiQuestion = z.infer<typeof ZSurveyMultipleChoiceMultiQuestion>;
|
||||
|
||||
Reference in New Issue
Block a user