From 47e948fbbcf369bed5ee9bb4641a5172dd5d94fc Mon Sep 17 00:00:00 2001 From: Carina Wollendorfer <30310907+CarinaWolli@users.noreply.github.com> Date: Fri, 3 Mar 2023 18:20:13 -0500 Subject: [PATCH] Fixes formatting issues with lists in event type description and bio (#7505) * fix ul and ol in editor * fix imports * disable list for team about section * fix event type description in list --------- Co-authored-by: CarinaWolli Co-authored-by: Peer Richelsen --- .../components/eventtype/EventSetupTab.tsx | 4 +--- .../steps-views/UserProfile.tsx | 4 +--- apps/web/components/team/screens/Team.tsx | 6 ++---- apps/web/pages/[user].tsx | 6 ++---- apps/web/pages/[user]/[type].tsx | 3 ++- .../web/pages/settings/my-account/profile.tsx | 5 ++--- apps/web/pages/team/[slug].tsx | 4 +--- .../ee/teams/pages/team-profile-view.tsx | 5 ++--- .../components/CreateEventTypeDialog.tsx | 12 +++++------- .../components/EventTypeDescription.tsx | 19 ++++++++----------- packages/lib/markdownIt.ts | 12 ++++++++++++ .../eventTypeDescriptionParseAndSanitize.ts | 3 +-- packages/ui/components/editor/Editor.tsx | 11 ++++++++++- .../ui/components/editor/stylesEditor.css | 3 ++- 14 files changed, 51 insertions(+), 46 deletions(-) create mode 100644 packages/lib/markdownIt.ts diff --git a/apps/web/components/eventtype/EventSetupTab.tsx b/apps/web/components/eventtype/EventSetupTab.tsx index 9e9f19eaa9..7b911fbe19 100644 --- a/apps/web/components/eventtype/EventSetupTab.tsx +++ b/apps/web/components/eventtype/EventSetupTab.tsx @@ -1,7 +1,6 @@ import { useAutoAnimate } from "@formkit/auto-animate/react"; import { zodResolver } from "@hookform/resolvers/zod"; import { isValidPhoneNumber } from "libphonenumber-js"; -import MarkdownIt from "markdown-it"; import { Trans } from "next-i18next"; import Link from "next/link"; import type { EventTypeSetupProps, FormValues } from "pages/event-types/[type]"; @@ -14,6 +13,7 @@ import type { EventLocationType } from "@calcom/app-store/locations"; import { getEventLocationType, MeetLocationType, LocationType } from "@calcom/app-store/locations"; import { CAL_URL } from "@calcom/lib/constants"; import { useLocale } from "@calcom/lib/hooks/useLocale"; +import { md } from "@calcom/lib/markdownIt"; import { slugify } from "@calcom/lib/slugify"; import turndown from "@calcom/lib/turndownService"; import { Button, Editor, Label, Select, SettingsToggle, Skeleton, TextField } from "@calcom/ui"; @@ -23,8 +23,6 @@ import { EditLocationDialog } from "@components/dialog/EditLocationDialog"; import type { SingleValueLocationOption, LocationOption } from "@components/ui/form/LocationSelect"; import LocationSelect from "@components/ui/form/LocationSelect"; -const md = new MarkdownIt("default", { html: true, breaks: true, linkify: true }); - const getLocationFromType = ( type: EventLocationType["type"], locationOptions: Pick["locationOptions"] diff --git a/apps/web/components/getting-started/steps-views/UserProfile.tsx b/apps/web/components/getting-started/steps-views/UserProfile.tsx index d6c1a5b1d9..db45f9a3f8 100644 --- a/apps/web/components/getting-started/steps-views/UserProfile.tsx +++ b/apps/web/components/getting-started/steps-views/UserProfile.tsx @@ -1,11 +1,11 @@ import { ArrowRightIcon } from "@heroicons/react/solid"; -import MarkdownIt from "markdown-it"; import { useRouter } from "next/router"; import type { FormEvent } from "react"; import { useRef, useState } from "react"; import { useForm } from "react-hook-form"; import { useLocale } from "@calcom/lib/hooks/useLocale"; +import { md } from "@calcom/lib/markdownIt"; import { telemetryEventTypes, useTelemetry } from "@calcom/lib/telemetry"; import turndown from "@calcom/lib/turndownService"; import { trpc } from "@calcom/trpc/react"; @@ -14,8 +14,6 @@ import { Avatar } from "@calcom/ui"; import type { IOnboardingPageProps } from "../../../pages/getting-started/[[...step]]"; -const md = new MarkdownIt("default", { html: true, breaks: true, linkify: true }); - type FormData = { bio: string; }; diff --git a/apps/web/components/team/screens/Team.tsx b/apps/web/components/team/screens/Team.tsx index f8b245ac9e..0633bae939 100644 --- a/apps/web/components/team/screens/Team.tsx +++ b/apps/web/components/team/screens/Team.tsx @@ -1,13 +1,11 @@ -import MarkdownIt from "markdown-it"; import Link from "next/link"; import type { TeamPageProps } from "pages/team/[slug]"; import { WEBAPP_URL } from "@calcom/lib/constants"; import { useLocale } from "@calcom/lib/hooks/useLocale"; +import { md } from "@calcom/lib/markdownIt"; import { Avatar } from "@calcom/ui"; -const md = new MarkdownIt("default", { html: true, breaks: true, linkify: true }); - type TeamType = TeamPageProps["team"]; type MembersType = TeamType["members"]; type MemberType = MembersType[number]; @@ -19,7 +17,7 @@ const Member = ({ member, teamName }: { member: MemberType; teamName: string | n return ( -
+
& EmbedProps) { const { users, profile, eventTypes, isDynamicGroup, dynamicNames, dynamicUsernames, isSingleUser } = props; const [user] = users; //To be used when we only have a single user, not dynamic group @@ -147,7 +145,7 @@ export default function User(props: inferSSRProps & E {!isBioEmpty && ( <>
diff --git a/apps/web/pages/[user]/[type].tsx b/apps/web/pages/[user]/[type].tsx index a96ecf1b29..b0879a49ce 100644 --- a/apps/web/pages/[user]/[type].tsx +++ b/apps/web/pages/[user]/[type].tsx @@ -5,6 +5,7 @@ import type { LocationObject } from "@calcom/app-store/locations"; import { IS_TEAM_BILLING_ENABLED, WEBAPP_URL } from "@calcom/lib/constants"; import hasKeyInMetadata from "@calcom/lib/hasKeyInMetadata"; import { useLocale } from "@calcom/lib/hooks/useLocale"; +import { addListFormatting } from "@calcom/lib/markdownIt"; import type { User } from "@calcom/prisma/client"; import { isBrandingHidden } from "@lib/isBrandingHidden"; @@ -152,7 +153,7 @@ async function getUserPageProps(context: GetStaticPropsContext) { metadata: EventTypeMetaDataSchema.parse(eventType.metadata || {}), recurringEvent: parseRecurringEvent(eventType.recurringEvent), locations: privacyFilteredLocations(locations), - descriptionAsSafeHTML: eventType.description ? md.render(eventType.description) : null, + descriptionAsSafeHTML: eventType.description ? addListFormatting(md.render(eventType.description)) : null, }); // Check if the user you are logging into has any active teams or premium user name const hasActiveTeam = diff --git a/apps/web/pages/settings/my-account/profile.tsx b/apps/web/pages/settings/my-account/profile.tsx index bda681715e..5b695688d5 100644 --- a/apps/web/pages/settings/my-account/profile.tsx +++ b/apps/web/pages/settings/my-account/profile.tsx @@ -1,6 +1,5 @@ import { IdentityProvider } from "@prisma/client"; import crypto from "crypto"; -import MarkdownIt from "markdown-it"; import { signOut } from "next-auth/react"; import type { BaseSyntheticEvent } from "react"; import { useRef, useState } from "react"; @@ -10,6 +9,7 @@ import { getLayout } from "@calcom/features/settings/layouts/SettingsLayout"; import { ErrorCode } from "@calcom/lib/auth"; import { APP_NAME } from "@calcom/lib/constants"; import { useLocale } from "@calcom/lib/hooks/useLocale"; +import { md } from "@calcom/lib/markdownIt"; import turndown from "@calcom/lib/turndownService"; import type { TRPCClientErrorLike } from "@calcom/trpc/client"; import { trpc } from "@calcom/trpc/react"; @@ -41,8 +41,6 @@ import { FiAlertTriangle, FiTrash2 } from "@calcom/ui/components/icon"; import TwoFactor from "@components/auth/TwoFactor"; import { UsernameAvailabilityField } from "@components/ui/UsernameAvailability"; -const md = new MarkdownIt("default", { html: true, breaks: true, linkify: true }); - const SkeletonLoader = ({ title, description }: { title: string; description: string }) => { return ( @@ -371,6 +369,7 @@ const ProfileForm = ({ formMethods.setValue("bio", turndown(value), { shouldDirty: true }); }} excludedToolbarItems={["blockType"]} + disableLists />

{t("team_description")}

diff --git a/packages/features/eventtypes/components/CreateEventTypeDialog.tsx b/packages/features/eventtypes/components/CreateEventTypeDialog.tsx index 13dd714b7d..3773eed216 100644 --- a/packages/features/eventtypes/components/CreateEventTypeDialog.tsx +++ b/packages/features/eventtypes/components/CreateEventTypeDialog.tsx @@ -1,7 +1,6 @@ import { zodResolver } from "@hookform/resolvers/zod"; import { SchedulingType } from "@prisma/client"; import { isValidPhoneNumber } from "libphonenumber-js"; -import MarkdownIt from "markdown-it"; import { useRouter } from "next/router"; import { useForm } from "react-hook-form"; import { z } from "zod"; @@ -9,6 +8,7 @@ import { z } from "zod"; import { useLocale } from "@calcom/lib/hooks/useLocale"; import { useTypedQuery } from "@calcom/lib/hooks/useTypedQuery"; import { HttpError } from "@calcom/lib/http-error"; +import { md } from "@calcom/lib/markdownIt"; import slugify from "@calcom/lib/slugify"; import turndown from "@calcom/lib/turndownService"; import { createEventTypeInput } from "@calcom/prisma/zod/custom/eventtype"; @@ -26,8 +26,6 @@ import { Editor, } from "@calcom/ui"; -const md = new MarkdownIt("default", { html: true, breaks: true, linkify: true }); - // this describes the uniform data needed to create a new event type on Profile or Team export interface EventTypeParent { teamId: number | null | undefined; // if undefined, then it's a profile @@ -203,26 +201,26 @@ export default function CreateEventTypeDialog() { message={form.formState.errors.schedulingType.message} /> )} - + - {t("collective")} + {t("collective")}

{t("collective_description")}

- {t("round_robin")} + {t("round_robin")}

{t("round_robin_description")}

)} -
+
diff --git a/packages/features/eventtypes/components/EventTypeDescription.tsx b/packages/features/eventtypes/components/EventTypeDescription.tsx index 757401218c..a89611ba8d 100644 --- a/packages/features/eventtypes/components/EventTypeDescription.tsx +++ b/packages/features/eventtypes/components/EventTypeDescription.tsx @@ -1,14 +1,15 @@ -import { Prisma, SchedulingType } from "@prisma/client"; -import MarkdownIt from "markdown-it"; +import type { Prisma } from "@prisma/client"; +import { SchedulingType } from "@prisma/client"; import { useMemo } from "react"; import { FormattedNumber, IntlProvider } from "react-intl"; -import { z } from "zod"; +import type { z } from "zod"; import { classNames, parseRecurringEvent } from "@calcom/lib"; import getPaymentAppData from "@calcom/lib/getPaymentAppData"; import { useLocale } from "@calcom/lib/hooks/useLocale"; -import { baseEventTypeSelect } from "@calcom/prisma"; -import { EventTypeModel } from "@calcom/prisma/zod"; +import { addListFormatting, md } from "@calcom/lib/markdownIt"; +import type { baseEventTypeSelect } from "@calcom/prisma"; +import type { EventTypeModel } from "@calcom/prisma/zod"; import { Badge } from "@calcom/ui"; import { FiClock, @@ -29,11 +30,9 @@ export type EventTypeDescriptionProps = { seatsPerTimeSlot?: number; }; className?: string; - shortenDescription?: true; + shortenDescription?: boolean; }; -const md = new MarkdownIt("default", { html: true, breaks: false, linkify: true }); - export const EventTypeDescription = ({ eventType, className, @@ -58,9 +57,7 @@ export const EventTypeDescription = ({ shortenDescription ? "line-clamp-4" : "" )} dangerouslySetInnerHTML={{ - __html: shortenDescription - ? md.render(eventType.description?.replace(/


<\/p>|\n/g, " ")) - : md.render(eventType.description), + __html: addListFormatting(md.render(eventType.description)), }} /> )} diff --git a/packages/lib/markdownIt.ts b/packages/lib/markdownIt.ts new file mode 100644 index 0000000000..45df9a5fdd --- /dev/null +++ b/packages/lib/markdownIt.ts @@ -0,0 +1,12 @@ +import MarkdownIt from "markdown-it"; + +export const md = new MarkdownIt("default", { html: true, breaks: true, linkify: true }); + +export function addListFormatting(html: string) { + return html + .replaceAll("

    ", "
      ") + .replaceAll( + "
        ", + "
          " + ); +} diff --git a/packages/prisma/middleware/eventTypeDescriptionParseAndSanitize.ts b/packages/prisma/middleware/eventTypeDescriptionParseAndSanitize.ts index 4f21a8873c..ab561e1f20 100644 --- a/packages/prisma/middleware/eventTypeDescriptionParseAndSanitize.ts +++ b/packages/prisma/middleware/eventTypeDescriptionParseAndSanitize.ts @@ -1,7 +1,6 @@ import type { PrismaClient, EventType } from "@prisma/client"; -import MarkdownIt from "markdown-it"; -const md = new MarkdownIt("default", { html: true, breaks: true, linkify: true }); +import { md } from "@calcom/lib/markdownIt"; function parseAndSanitize(description: string) { const parsedMarkdown = md.render(description); diff --git a/packages/ui/components/editor/Editor.tsx b/packages/ui/components/editor/Editor.tsx index 1ae470d3d9..d66cdeeb96 100644 --- a/packages/ui/components/editor/Editor.tsx +++ b/packages/ui/components/editor/Editor.tsx @@ -31,6 +31,7 @@ export type TextEditorProps = { variables?: string[]; height?: string; placeholder?: string; + disableLists?: boolean; }; const editorConfig = { @@ -74,7 +75,15 @@ export const Editor = (props: TextEditorProps) => { - + { + if (index !== 3 && index !== 4) return value; + }) + : TRANSFORMERS + } + />
diff --git a/packages/ui/components/editor/stylesEditor.css b/packages/ui/components/editor/stylesEditor.css index 65c89ef81e..7a34014110 100644 --- a/packages/ui/components/editor/stylesEditor.css +++ b/packages/ui/components/editor/stylesEditor.css @@ -26,7 +26,7 @@ text-align: left; border-color: #D1D5DB; border-width: 1px; - padding: 1px + padding: 1px; } .editor-inner { @@ -37,6 +37,7 @@ overflow: scroll; resize: vertical; height: auto; + min-height: 40px; } .editor-input {