This is an automated email from the ASF dual-hosted git repository.

jscheffl pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/airflow.git


The following commit(s) were added to refs/heads/main by this push:
     new c692b0836b0 Trim the oversized text in the variables table and 
Implement variables view (#45401)
c692b0836b0 is described below

commit c692b0836b0db0df2b3352cc067c565603c7bfac
Author: Shubham Raj <[email protected]>
AuthorDate: Sun Jan 5 15:44:00 2025 +0530

    Trim the oversized text in the variables table and Implement variables view 
(#45401)
    
    * trim text component
    
    * fix
    
    * reviews
---
 .../ManageVariable/DeleteVariableButton.tsx        |   2 +-
 airflow/ui/src/pages/Variables/Variables.tsx       |   4 +
 airflow/ui/src/utils/TrimText.tsx                  | 118 +++++++++++++++++++++
 3 files changed, 123 insertions(+), 1 deletion(-)

diff --git 
a/airflow/ui/src/pages/Variables/ManageVariable/DeleteVariableButton.tsx 
b/airflow/ui/src/pages/Variables/ManageVariable/DeleteVariableButton.tsx
index c53dc77c670..d8d2efbecae 100644
--- a/airflow/ui/src/pages/Variables/ManageVariable/DeleteVariableButton.tsx
+++ b/airflow/ui/src/pages/Variables/ManageVariable/DeleteVariableButton.tsx
@@ -63,7 +63,7 @@ const DeleteVariableButton = ({ deleteKey: variableKey }: 
Props) => {
         <Dialog.Content backdrop>
           <Dialog.Header>
             <VStack align="start" gap={4}>
-              <Heading size="xl">Delete Variable - {variableKey} </Heading>
+              <Heading size="xl">Delete Variable</Heading>
             </VStack>
           </Dialog.Header>
 
diff --git a/airflow/ui/src/pages/Variables/Variables.tsx 
b/airflow/ui/src/pages/Variables/Variables.tsx
index 6f46deca343..bfe266a21a5 100644
--- a/airflow/ui/src/pages/Variables/Variables.tsx
+++ b/airflow/ui/src/pages/Variables/Variables.tsx
@@ -33,6 +33,7 @@ import { Button, Tooltip } from "src/components/ui";
 import { ActionBar } from "src/components/ui/ActionBar";
 import { Checkbox } from "src/components/ui/Checkbox";
 import { SearchParamsKeys, type SearchParamsKeysType } from 
"src/constants/searchParams";
+import { TrimText } from "src/utils/TrimText";
 
 import ImportVariablesButton from "./ImportVariablesButton";
 import AddVariableButton from "./ManageVariable/AddVariableButton";
@@ -63,14 +64,17 @@ const getColumns = ({
   },
   {
     accessorKey: "key",
+    cell: ({ row }) => <TrimText isClickable onClickContent={row.original} 
text={row.original.key} />,
     header: "Key",
   },
   {
     accessorKey: "value",
+    cell: ({ row }) => <TrimText showTooltip text={row.original.value} />,
     header: "Value",
   },
   {
     accessorKey: "description",
+    cell: ({ row }) => <TrimText showTooltip text={row.original.description} 
/>,
     header: "Description",
   },
   {
diff --git a/airflow/ui/src/utils/TrimText.tsx 
b/airflow/ui/src/utils/TrimText.tsx
new file mode 100644
index 00000000000..d9cdb5ae002
--- /dev/null
+++ b/airflow/ui/src/utils/TrimText.tsx
@@ -0,0 +1,118 @@
+/*!
+ * 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 { Text, Box, useDisclosure, Heading, Stack } from "@chakra-ui/react";
+
+import { Dialog, Tooltip } from "src/components/ui";
+
+import { capitalize } from "./capitalize";
+
+const formatKey = (key: string): string => {
+  const formatted = capitalize(key).replaceAll("_", " ");
+
+  return formatted;
+};
+
+type TrimTextProps = {
+  readonly charLimit?: number;
+  readonly isClickable?: boolean;
+  readonly onClickContent?: Record<string, boolean | number | string | null>;
+  readonly showTooltip?: boolean;
+  readonly text: string | null;
+};
+
+export const TrimText = ({
+  charLimit = 50,
+  isClickable = false,
+  onClickContent,
+  showTooltip = false,
+  text,
+}: TrimTextProps) => {
+  const safeText = text ?? "";
+  const isTrimmed = safeText.length > charLimit;
+  const trimmedText = isTrimmed ? `${safeText.slice(0, charLimit)}...` : 
safeText;
+
+  const { onClose, onOpen, open } = useDisclosure();
+
+  return (
+    <>
+      <Tooltip
+        content={showTooltip && isTrimmed ? safeText : undefined}
+        disabled={!isTrimmed || !showTooltip}
+      >
+        <Box
+          _hover={isClickable ? { textDecoration: "underline" } : undefined}
+          as={isClickable ? "button" : "span"}
+          color={isClickable ? "fg.info" : undefined}
+          cursor={isClickable ? "pointer" : "default"}
+          fontWeight={isClickable ? "bold" : undefined}
+          onClick={onOpen}
+        >
+          <Text>{trimmedText}</Text>
+        </Box>
+      </Tooltip>
+
+      <Dialog.Root onOpenChange={onClose} open={isClickable ? open : 
undefined} size="xl">
+        <Dialog.Content backdrop>
+          <Dialog.Header>
+            <Heading size="xl">Details</Heading>
+          </Dialog.Header>
+
+          <Dialog.CloseTrigger />
+
+          <Dialog.Body>
+            <Stack gap={4}>
+              {onClickContent ? (
+                Object.entries(onClickContent).map(([key, value]) => {
+                  const formattedKey = formatKey(key);
+                  const isEmpty = value === "" || value === null;
+
+                  return (
+                    <Box key={key}>
+                      <Text fontWeight="bold" mb={2}>
+                        {formattedKey}
+                      </Text>
+                      <Box
+                        bg="gray.subtle"
+                        borderRadius="md"
+                        maxHeight={200}
+                        overflow="auto"
+                        overflowWrap="break-word"
+                        p={4}
+                        whiteSpace="pre-wrap"
+                      >
+                        <Text
+                          color={isEmpty ? "gray.emphasized" : undefined}
+                          fontWeight={isEmpty ? "bold" : "normal"}
+                        >
+                          {isEmpty ? "Empty" : String(value)}
+                        </Text>
+                      </Box>
+                    </Box>
+                  );
+                })
+              ) : (
+                <Text>No content available.</Text>
+              )}
+            </Stack>
+          </Dialog.Body>
+        </Dialog.Content>
+      </Dialog.Root>
+    </>
+  );
+};

Reply via email to