feat: Support adding links to checkbox type question using markdown[Blocked on another PR] (#8943)
* Support adding links in boolean checkbox * Fix and comment * remove textarea, it was accidentally pushed.
This commit is contained in:
1 parent
d353904c7c
commit
61f1a3b21e
8 files changed
+65
-8
No files matched your search
@@ -1,6 +1,8 @@
|
||||
import type { EventTypeCustomInput, EventType, Prisma, Workflow } from "@prisma/client";
|
||||
import type { z } from "zod";
|
||||
|
||||
import { fieldsThatSupportLabelAsSafeHtml } from "@calcom/features/form-builder/fieldsThatSupportLabelAsSafeHtml";
|
||||
import { markdownToSafeHTML } from "@calcom/lib/markdownToSafeHTML";
|
||||
import slugify from "@calcom/lib/slugify";
|
||||
import { EventTypeCustomInputType } from "@calcom/prisma/enums";
|
||||
import {
|
||||
@@ -328,7 +330,16 @@ export const ensureBookingInputsHaveSystemFields = ({
|
||||
};
|
||||
}
|
||||
}
|
||||
bookingFields = bookingFields.concat(missingSystemAfterFields);
|
||||
|
||||
bookingFields = bookingFields.concat(missingSystemAfterFields).map((f) => {
|
||||
return {
|
||||
...f,
|
||||
// TODO: This has to be a FormBuilder feature and not be specific to bookingFields. Either use zod transform in FormBuilder to add labelAsSafeHtml automatically or add a getter for fields that would do this.
|
||||
...(fieldsThatSupportLabelAsSafeHtml.includes(f.type)
|
||||
? { labelAsSafeHtml: markdownToSafeHTML(f.label || null) || "" }
|
||||
: null),
|
||||
};
|
||||
});
|
||||
|
||||
return eventTypeBookingFields.brand<"HAS_SYSTEM_FIELDS">().parse(bookingFields);
|
||||
};
|
||||
@@ -480,7 +480,9 @@ export const Components: Record<FieldType, Component> = {
|
||||
placeholder=""
|
||||
checked={value}
|
||||
disabled={readOnly}
|
||||
description={label ?? ""}
|
||||
description=""
|
||||
// Form Builder ensures that it would be safe HTML in here if the field type supports it. So, we can safely use label value in `descriptionAsSafeHtml`
|
||||
descriptionAsSafeHtml={label ?? ""}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -6,6 +6,7 @@ import type { z } from "zod";
|
||||
|
||||
import { classNames } from "@calcom/lib";
|
||||
import { useLocale } from "@calcom/lib/hooks/useLocale";
|
||||
import { markdownToSafeHTML } from "@calcom/lib/markdownToSafeHTML";
|
||||
import {
|
||||
Label,
|
||||
Badge,
|
||||
@@ -26,6 +27,7 @@ import {
|
||||
import { ArrowDown, ArrowUp, X, Plus, Trash2 } from "@calcom/ui/components/icon";
|
||||
|
||||
import { fieldTypesConfigMap } from "./fieldTypes";
|
||||
import { fieldsThatSupportLabelAsSafeHtml } from "./fieldsThatSupportLabelAsSafeHtml";
|
||||
import type { fieldsSchema } from "./schema";
|
||||
import { getVariantsConfig } from "./utils";
|
||||
|
||||
@@ -530,7 +532,18 @@ function FieldLabel({ field }: { field: RhfFormField }) {
|
||||
const variantsConfig = field.variantsConfig;
|
||||
const defaultVariant = fieldTypeConfigVariantsConfig?.defaultVariant;
|
||||
if (!fieldTypeConfigVariants || !variantsConfig) {
|
||||
return <span>{field.label || t(field.defaultLabel || "")}</span>;
|
||||
if (fieldsThatSupportLabelAsSafeHtml.includes(field.type)) {
|
||||
return (
|
||||
<span
|
||||
dangerouslySetInnerHTML={{
|
||||
// Derive from field.label because label might change in b/w and field.labelAsSafeHtml will not be updated.
|
||||
__html: markdownToSafeHTML(field.label || "") || t(field.defaultLabel || ""),
|
||||
}}
|
||||
/>
|
||||
);
|
||||
} else {
|
||||
return <span>{field.label || t(field.defaultLabel || "")}</span>;
|
||||
}
|
||||
}
|
||||
const variant = field.variant || defaultVariant;
|
||||
if (!variant) {
|
||||
|
||||
@@ -10,6 +10,7 @@ import { Info } from "@calcom/ui/components/icon";
|
||||
|
||||
import { Components, isValidValueProp } from "./Components";
|
||||
import { fieldTypesConfigMap } from "./fieldTypes";
|
||||
import { fieldsThatSupportLabelAsSafeHtml } from "./fieldsThatSupportLabelAsSafeHtml";
|
||||
import type { fieldsSchema } from "./schema";
|
||||
import { getVariantsConfig } from "./utils";
|
||||
|
||||
@@ -134,7 +135,7 @@ const WithLabel = ({
|
||||
<div className="mb-2 flex items-center">
|
||||
<Label className="!mb-0">
|
||||
<span>{field.label}</span>
|
||||
<span className="text-emphasis ml-1 -mb-1 text-sm font-medium leading-none">
|
||||
<span className="text-emphasis -mb-1 ml-1 text-sm font-medium leading-none">
|
||||
{!readOnly && field.required ? "*" : ""}
|
||||
</span>
|
||||
</Label>
|
||||
@@ -169,7 +170,14 @@ function getAndUpdateNormalizedValues(field: RhfFormFields[number], t: TFunction
|
||||
}
|
||||
}
|
||||
|
||||
const label = noLabel ? "" : field.label || t(field.defaultLabel || "");
|
||||
/**
|
||||
* Instead of passing labelAsSafeHtml props to all the components, FormBuilder components can assume that the label is safe html and use it on a case by case basis after adding checks here
|
||||
*/
|
||||
if (fieldsThatSupportLabelAsSafeHtml.includes(field.type) && field.labelAsSafeHtml === undefined) {
|
||||
throw new Error(`${field.name}:${field.type} type must have labelAsSafeHtml set`);
|
||||
}
|
||||
|
||||
const label = noLabel ? "" : field.labelAsSafeHtml || field.label || t(field.defaultLabel || "");
|
||||
const placeholder = field.placeholder || t(field.defaultPlaceholder || "");
|
||||
|
||||
if (field.variantsConfig?.variants) {
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
import type { FieldType } from "./schema";
|
||||
|
||||
/**
|
||||
* Once a component supports `labelAsSafeHtml`, add it's field's type here
|
||||
* A whitelist is needed because unless we explicitly use dangerouslySetInnerHTML, React will escape the HTML
|
||||
*/
|
||||
export const fieldsThatSupportLabelAsSafeHtml: FieldType[] = ["boolean"];
|
||||
@@ -35,6 +35,8 @@ const baseFieldSchema = z.object({
|
||||
type: fieldTypeEnum,
|
||||
// TODO: We should make at least one of `defaultPlaceholder` and `placeholder` required. Do the same for label.
|
||||
label: z.string().optional(),
|
||||
labelAsSafeHtml: z.string().optional(),
|
||||
|
||||
/**
|
||||
* It is the default label that will be used when a new field is created.
|
||||
* Note: It belongs in FieldsTypeConfig, so that changing defaultLabel in code can work for existing fields as well(for fields that are using the default label).
|
||||
|
||||
@@ -17,7 +17,8 @@ export function markdownToSafeHTML(markdown: string | null) {
|
||||
.replace(
|
||||
/<ol>/g,
|
||||
"<ol style='list-style-type: decimal; list-style-position: inside; margin-left: 12px; margin-bottom: 4px'>"
|
||||
);
|
||||
)
|
||||
.replace(/<a\s+href=/g, "<a target='_blank' class='text-blue-500 hover:text-blue-600' href=");
|
||||
|
||||
return safeHTMLWithListFormatting;
|
||||
}
|
||||
@@ -14,6 +14,10 @@ type Props = InputHTMLAttributes<HTMLInputElement> & {
|
||||
error?: boolean;
|
||||
className?: string;
|
||||
descriptionClassName?: string;
|
||||
/**
|
||||
* Accepts this special property instead of allowing description itself to be accidentally used in dangerous way.
|
||||
*/
|
||||
descriptionAsSafeHtml?: string;
|
||||
};
|
||||
|
||||
const Checkbox = React.forwardRef<
|
||||
@@ -35,7 +39,7 @@ const Checkbox = React.forwardRef<
|
||||
Checkbox.displayName = CheckboxPrimitive.Root.displayName;
|
||||
|
||||
const CheckboxField = forwardRef<HTMLInputElement, Props>(
|
||||
({ label, description, error, disabled, ...rest }, ref) => {
|
||||
({ label, description, error, disabled, descriptionAsSafeHtml, ...rest }, ref) => {
|
||||
const descriptionAsLabel = !label || rest.descriptionAsLabel;
|
||||
const id = useId();
|
||||
return (
|
||||
@@ -86,7 +90,16 @@ const CheckboxField = forwardRef<HTMLInputElement, Props>(
|
||||
)}
|
||||
/>
|
||||
</div>
|
||||
<span className={classNames("text-sm", rest.descriptionClassName)}>{description}</span>
|
||||
{descriptionAsSafeHtml ? (
|
||||
<span
|
||||
className={classNames("text-sm", rest.descriptionClassName)}
|
||||
dangerouslySetInnerHTML={{
|
||||
__html: descriptionAsSafeHtml,
|
||||
}}
|
||||
/>
|
||||
) : (
|
||||
<span className={classNames("text-sm", rest.descriptionClassName)}>{description}</span>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
{/* {informationIconText && <InfoBadge content={informationIconText}></InfoBadge>} */}
|
||||
|
||||
Reference in new issue
Block a user