From 3fc73229da651420cd2b974e1fff9d9786d09ec1 Mon Sep 17 00:00:00 2001 From: Pierre Jeambrun Date: Thu, 9 Jan 2025 00:13:36 +0800 Subject: [PATCH] AIP-38 Fix markdown rendering due to ChakraUI CSS reset (#45459) --- airflow/ui/rules/react.js | 2 +- .../ClearRun/ClearRunTaskAccordion.tsx | 8 +- .../ui/src/components/DocumentationModal.tsx | 6 +- airflow/ui/src/components/ReactMarkdown.tsx | 117 ++++++++++++++++++ 4 files changed, 125 insertions(+), 8 deletions(-) create mode 100644 airflow/ui/src/components/ReactMarkdown.tsx diff --git a/airflow/ui/rules/react.js b/airflow/ui/rules/react.js index fe4432d3fbde6..6483a2e5579b1 100644 --- a/airflow/ui/rules/react.js +++ b/airflow/ui/rules/react.js @@ -546,7 +546,7 @@ export const reactRules = /** @type {const} @satisfies {FlatConfig.Config} */ ({ * * @see [react/no-multi-comp](https://github.com/jsx-eslint/eslint-plugin-react/blob/HEAD/docs/rules/no-multi-comp.md) */ - [`${reactNamespace}/no-multi-comp`]: WARN, + [`${reactNamespace}/no-multi-comp`]: [WARN, { ignoreStateless: true }], /** * Enforce that namespaces are not used in React elements. diff --git a/airflow/ui/src/components/ClearRun/ClearRunTaskAccordion.tsx b/airflow/ui/src/components/ClearRun/ClearRunTaskAccordion.tsx index 77c51226e5e9a..b8dadb71c0ac3 100644 --- a/airflow/ui/src/components/ClearRun/ClearRunTaskAccordion.tsx +++ b/airflow/ui/src/components/ClearRun/ClearRunTaskAccordion.tsx @@ -20,7 +20,6 @@ import { Box, Editable, Text, VStack } from "@chakra-ui/react"; import { Link } from "@chakra-ui/react"; import type { ColumnDef } from "@tanstack/react-table"; import type { ChangeEvent } from "react"; -import Markdown from "react-markdown"; import { Link as RouterLink } from "react-router-dom"; import type { @@ -29,12 +28,12 @@ import type { TaskInstanceResponse, } from "openapi/requests/types.gen"; import { DataTable } from "src/components/DataTable"; +import ReactMarkdown from "src/components/ReactMarkdown"; import { Status, Tooltip } from "src/components/ui"; +import { Accordion } from "src/components/ui"; import { getTaskInstanceLink } from "src/utils/links"; import { trimText } from "src/utils/trimTextFn"; -import { Accordion } from "../ui"; - const columns: Array> = [ { accessorKey: "task_display_name", @@ -116,6 +115,7 @@ const ClearRunTasksAccordion = ({ affectedTasks, note, setNote }: Props) => ( value={note ?? ""} > ( width="100%" > {Boolean(note) ? ( - {note} + {note} ) : ( Add a note... diff --git a/airflow/ui/src/components/DocumentationModal.tsx b/airflow/ui/src/components/DocumentationModal.tsx index b143aa833c9d3..95ca9b83210c3 100644 --- a/airflow/ui/src/components/DocumentationModal.tsx +++ b/airflow/ui/src/components/DocumentationModal.tsx @@ -19,11 +19,11 @@ import { Box, Heading } from "@chakra-ui/react"; import { useState } from "react"; import { FiBookOpen } from "react-icons/fi"; -import Markdown from "react-markdown"; -import remarkGfm from "remark-gfm"; import { Button, Dialog } from "src/components/ui"; +import ReactMarkdown from "./ReactMarkdown"; + const DocumentationModal = ({ docMd, docType }: { readonly docMd: string; readonly docType: string }) => { const [isDocsOpen, setIsDocsOpen] = useState(false); @@ -40,7 +40,7 @@ const DocumentationModal = ({ docMd, docType }: { readonly docMd: string; readon - {docMd} + {docMd} diff --git a/airflow/ui/src/components/ReactMarkdown.tsx b/airflow/ui/src/components/ReactMarkdown.tsx new file mode 100644 index 0000000000000..104e2e100f13a --- /dev/null +++ b/airflow/ui/src/components/ReactMarkdown.tsx @@ -0,0 +1,117 @@ +/*! + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +import { + Box, + Code, + Separator, + Heading, + Image, + type ImageProps, + Link, + List, + Table, + Text, +} from "@chakra-ui/react"; +import type { PropsWithChildren, ReactNode } from "react"; +import type { Components, Options } from "react-markdown"; +import ReactMD from "react-markdown"; +import remarkGfm from "remark-gfm"; + +const fontSizeMapping = { + h1: "1.5em", + h2: "1.25em", + h3: "1.125em", + h4: "1em", + h5: "0.875em", + h6: "0.75em", +}; + +const makeHeading = + (header: keyof typeof fontSizeMapping) => + ({ children, ...props }: PropsWithChildren) => ( + + {children} + + ); + +const components = { + // eslint-disable-next-line id-length + a: ({ children, href, title }: { children: ReactNode; href: string; title?: string }) => ( + + {children} + + ), + blockquote: ({ children }: PropsWithChildren) => ( + + {children} + + ), + code: ({ children, className, inline }: { children: ReactNode; className?: string; inline?: boolean }) => { + if (inline) { + return ( + + {children} + + ); + } + + return ( + + {children} + + ); + }, + del: ({ children }: PropsWithChildren) => {children}, + em: ({ children }: PropsWithChildren) => {children}, + h1: makeHeading("h1"), + h2: makeHeading("h2"), + h3: makeHeading("h3"), + h4: makeHeading("h4"), + h5: makeHeading("h5"), + h6: makeHeading("h6"), + hr: () => , + img: (props: ImageProps) => , + li: ({ children }: PropsWithChildren) => {children}, + ol: ({ children }: PropsWithChildren) => ( + + {children} + + ), + // eslint-disable-next-line id-length + p: ({ children }: PropsWithChildren) => {children}, + pre: ({ children }: PropsWithChildren) => {children}, + table: ({ children }: PropsWithChildren) => {children}, + tbody: Table.Body, + td: Table.Cell, + text: ({ children }: PropsWithChildren) => {children}, + th: Table.ColumnHeader, + thead: Table.Header, + tr: Table.Row, + ul: ({ children }: PropsWithChildren) => ( + + {children} + + ), +}; + +const ReactMarkdown = (props: Options) => ( + +); + +export default ReactMarkdown;