From 61f1a3b21ebe56b7264f013969efd5137013dccf Mon Sep 17 00:00:00 2001 From: Hariom Balhara Date: Fri, 21 Jul 2023 23:30:28 +0530 Subject: [PATCH] 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. --- .../features/bookings/lib/getBookingFields.ts | 13 ++++++++++++- packages/features/form-builder/Components.tsx | 4 +++- packages/features/form-builder/FormBuilder.tsx | 15 ++++++++++++++- .../features/form-builder/FormBuilderField.tsx | 12 ++++++++++-- .../fieldsThatSupportLabelAsSafeHtml.ts | 7 +++++++ packages/features/form-builder/schema.ts | 2 ++ packages/lib/markdownToSafeHTML.ts | 3 ++- .../ui/components/form/checkbox/Checkbox.tsx | 17 +++++++++++++++-- 8 files changed, 65 insertions(+), 8 deletions(-) create mode 100644 packages/features/form-builder/fieldsThatSupportLabelAsSafeHtml.ts diff --git a/packages/features/bookings/lib/getBookingFields.ts b/packages/features/bookings/lib/getBookingFields.ts index ffc72da8d3..d87d15afe0 100644 --- a/packages/features/bookings/lib/getBookingFields.ts +++ b/packages/features/bookings/lib/getBookingFields.ts @@ -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); }; diff --git a/packages/features/form-builder/Components.tsx b/packages/features/form-builder/Components.tsx index 0adc3f7cc0..2b3a0434f8 100644 --- a/packages/features/form-builder/Components.tsx +++ b/packages/features/form-builder/Components.tsx @@ -480,7 +480,9 @@ export const Components: Record = { 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 ?? ""} /> ); diff --git a/packages/features/form-builder/FormBuilder.tsx b/packages/features/form-builder/FormBuilder.tsx index cdf60ae772..516be815fb 100644 --- a/packages/features/form-builder/FormBuilder.tsx +++ b/packages/features/form-builder/FormBuilder.tsx @@ -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 {field.label || t(field.defaultLabel || "")}; + if (fieldsThatSupportLabelAsSafeHtml.includes(field.type)) { + return ( + + ); + } else { + return {field.label || t(field.defaultLabel || "")}; + } } const variant = field.variant || defaultVariant; if (!variant) { diff --git a/packages/features/form-builder/FormBuilderField.tsx b/packages/features/form-builder/FormBuilderField.tsx index aff18f16a2..f75f876611 100644 --- a/packages/features/form-builder/FormBuilderField.tsx +++ b/packages/features/form-builder/FormBuilderField.tsx @@ -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 = ({
@@ -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) { diff --git a/packages/features/form-builder/fieldsThatSupportLabelAsSafeHtml.ts b/packages/features/form-builder/fieldsThatSupportLabelAsSafeHtml.ts new file mode 100644 index 0000000000..a11598e3e2 --- /dev/null +++ b/packages/features/form-builder/fieldsThatSupportLabelAsSafeHtml.ts @@ -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"]; diff --git a/packages/features/form-builder/schema.ts b/packages/features/form-builder/schema.ts index 16b5778e76..7eddf7bf8c 100644 --- a/packages/features/form-builder/schema.ts +++ b/packages/features/form-builder/schema.ts @@ -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). diff --git a/packages/lib/markdownToSafeHTML.ts b/packages/lib/markdownToSafeHTML.ts index fd5945930b..a774e5498e 100644 --- a/packages/lib/markdownToSafeHTML.ts +++ b/packages/lib/markdownToSafeHTML.ts @@ -17,7 +17,8 @@ export function markdownToSafeHTML(markdown: string | null) { .replace( /
    /g, "
      " - ); + ) + .replace(/{description} + {descriptionAsSafeHtml ? ( + + ) : ( + {description} + )} )} {/* {informationIconText && } */}