Skip to content

Commit

Permalink
AIP-38 Fix markdown rendering due to ChakraUI CSS reset (apache#45459)
Browse files Browse the repository at this point in the history
  • Loading branch information
pierrejeambrun authored Jan 8, 2025
1 parent 32dc9e8 commit 3fc7322
Show file tree
Hide file tree
Showing 4 changed files with 125 additions and 8 deletions.
2 changes: 1 addition & 1 deletion airflow/ui/rules/react.js
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
8 changes: 4 additions & 4 deletions airflow/ui/src/components/ClearRun/ClearRunTaskAccordion.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand All @@ -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<ColumnDef<TaskInstanceResponse>> = [
{
accessorKey: "task_display_name",
Expand Down Expand Up @@ -116,6 +115,7 @@ const ClearRunTasksAccordion = ({ affectedTasks, note, setNote }: Props) => (
value={note ?? ""}
>
<Editable.Preview
_hover={{ backgroundColor: "transparent" }}
alignItems="flex-start"
as={VStack}
gap="0"
Expand All @@ -124,7 +124,7 @@ const ClearRunTasksAccordion = ({ affectedTasks, note, setNote }: Props) => (
width="100%"
>
{Boolean(note) ? (
<Markdown>{note}</Markdown>
<ReactMarkdown>{note}</ReactMarkdown>
) : (
<Text color="gray" opacity={0.6}>
Add a note...
Expand Down
6 changes: 3 additions & 3 deletions airflow/ui/src/components/DocumentationModal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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);

Expand All @@ -40,7 +40,7 @@ const DocumentationModal = ({ docMd, docType }: { readonly docMd: string; readon
<Dialog.CloseTrigger closeButtonProps={{ size: "xl" }} />
</Dialog.Header>
<Dialog.Body display="flex">
<Markdown remarkPlugins={[remarkGfm]}>{docMd}</Markdown>
<ReactMarkdown>{docMd}</ReactMarkdown>
</Dialog.Body>
</Dialog.Content>
</Dialog.Root>
Expand Down
117 changes: 117 additions & 0 deletions airflow/ui/src/components/ReactMarkdown.tsx
Original file line number Diff line number Diff line change
@@ -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) => (
<Heading as={header} fontSize={fontSizeMapping[header]} {...props} my={3}>
{children}
</Heading>
);

const components = {
// eslint-disable-next-line id-length
a: ({ children, href, title }: { children: ReactNode; href: string; title?: string }) => (
<Link color="blue.600" fontWeight="bold" href={href} title={title}>
{children}
</Link>
),
blockquote: ({ children }: PropsWithChildren) => (
<Box as="blockquote" borderColor="gray.400" borderLeft="solid 2px" fontStyle="italic" my={3} pl={2}>
{children}
</Box>
),
code: ({ children, className, inline }: { children: ReactNode; className?: string; inline?: boolean }) => {
if (inline) {
return (
<Code display="inline" p={2}>
{children}
</Code>
);
}

return (
<Code className={className} display="block" p={2} w="full" whiteSpace="break-spaces">
{children}
</Code>
);
},
del: ({ children }: PropsWithChildren) => <Text as="del">{children}</Text>,
em: ({ children }: PropsWithChildren) => <Text as="em">{children}</Text>,
h1: makeHeading("h1"),
h2: makeHeading("h2"),
h3: makeHeading("h3"),
h4: makeHeading("h4"),
h5: makeHeading("h5"),
h6: makeHeading("h6"),
hr: () => <Separator my={3} />,
img: (props: ImageProps) => <Image my={3} {...props} maxWidth="300px" />,
li: ({ children }: PropsWithChildren) => <List.Item>{children}</List.Item>,
ol: ({ children }: PropsWithChildren) => (
<List.Root as="ol" mb={3} pl={4}>
{children}
</List.Root>
),
// eslint-disable-next-line id-length
p: ({ children }: PropsWithChildren) => <Text>{children}</Text>,
pre: ({ children }: PropsWithChildren) => <Code my={3}>{children}</Code>,
table: ({ children }: PropsWithChildren) => <Table.Root mb={3}>{children}</Table.Root>,
tbody: Table.Body,
td: Table.Cell,
text: ({ children }: PropsWithChildren) => <Text as="span">{children}</Text>,
th: Table.ColumnHeader,
thead: Table.Header,
tr: Table.Row,
ul: ({ children }: PropsWithChildren) => (
<List.Root mb={3} pl={4}>
{children}
</List.Root>
),
};

const ReactMarkdown = (props: Options) => (
<ReactMD components={components as Components} {...props} remarkPlugins={[remarkGfm]} skipHtml />
);

export default ReactMarkdown;

0 comments on commit 3fc7322

Please sign in to comment.