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

bbovenzi pushed a commit to branch v3-0-test
in repository https://gitbox.apache.org/repos/asf/airflow.git


The following commit(s) were added to refs/heads/v3-0-test by this push:
     new faf298b5596 [v3-0-test] Add link to tag to filter dags associated with 
the tag. (#49680) (#50042)
faf298b5596 is described below

commit faf298b55964efece3d6a1a79159cdbd642455f9
Author: github-actions[bot] 
<41898282+github-actions[bot]@users.noreply.github.com>
AuthorDate: Wed Apr 30 14:14:10 2025 -0400

    [v3-0-test] Add link to tag to filter dags associated with the tag. 
(#49680) (#50042)
    
    * Add link to tag to filter dags by the tag.
    
    * Fix test by wrapping the component inside a router that is required for 
link.
    
    * Handle more than limited tags with interactive tooltip for remaining tag 
links.
    
    * Fix test.
    (cherry picked from commit 8b9259f694f89b30f8c0fda431e456fc184c807c)
    
    Co-authored-by: Karthikeyan Singaravelan <[email protected]>
---
 .../airflow/ui/src/components/LimitedItemsList.tsx | 23 +++++++++++++++-------
 .../airflow/ui/src/pages/Dag/DagHeader.test.tsx    | 10 +++++-----
 .../airflow/ui/src/pages/DagsList/DagCard.test.tsx |  2 +-
 .../src/airflow/ui/src/pages/DagsList/DagTags.tsx  |  8 +++++++-
 4 files changed, 29 insertions(+), 14 deletions(-)

diff --git a/airflow-core/src/airflow/ui/src/components/LimitedItemsList.tsx 
b/airflow-core/src/airflow/ui/src/components/LimitedItemsList.tsx
index 3c696beef16..7d993413c2d 100644
--- a/airflow-core/src/airflow/ui/src/components/LimitedItemsList.tsx
+++ b/airflow-core/src/airflow/ui/src/components/LimitedItemsList.tsx
@@ -16,25 +16,34 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-import { Box, Text, HStack } from "@chakra-ui/react";
+import { Box, Text, HStack, StackSeparator } from "@chakra-ui/react";
 import React, { type ReactNode } from "react";
 
 import { Tooltip } from "./ui";
 
 type ListProps = {
   readonly icon?: ReactNode;
+  readonly interactive?: boolean;
   readonly items: Array<ReactNode | string>;
   readonly maxItems?: number;
   readonly separator?: string;
 };
 
-export const LimitedItemsList = ({ icon, items, maxItems, separator = ", " }: 
ListProps) => {
+export const LimitedItemsList = ({
+  icon,
+  interactive = false,
+  items,
+  maxItems,
+  separator = ", ",
+}: ListProps) => {
   const shouldTruncate = maxItems !== undefined && items.length > maxItems;
   const displayItems = shouldTruncate ? items.slice(0, maxItems) : items;
   const remainingItems = shouldTruncate ? items.slice(maxItems) : [];
-  const remainingItemsList = remainingItems
-    .map((item) => (typeof item === "string" ? item : "item"))
-    .join(", ");
+  const remainingItemsList = interactive ? (
+    <HStack separator={<StackSeparator />}>{remainingItems}</HStack>
+  ) : (
+    `More items: ${remainingItems.map((item) => (typeof item === "string" ? 
item : "item")).join(", ")}`
+  );
 
   if (!items.length) {
     return undefined;
@@ -57,9 +66,9 @@ export const LimitedItemsList = ({ icon, items, maxItems, 
separator = ", " }: Li
           remainingItems.length === 1 ? (
             <Text as="span">{remainingItems[0]}</Text>
           ) : (
-            <Tooltip content={`More items: ${remainingItemsList}`}>
+            <Tooltip content={remainingItemsList} interactive={interactive}>
               <Text as="span" cursor="help">
-                , +{remainingItems.length} more
+                +{remainingItems.length} more
               </Text>
             </Tooltip>
           )
diff --git a/airflow-core/src/airflow/ui/src/pages/Dag/DagHeader.test.tsx 
b/airflow-core/src/airflow/ui/src/pages/Dag/DagHeader.test.tsx
index 4da3d6651b1..b0c57f4a4f7 100644
--- a/airflow-core/src/airflow/ui/src/pages/Dag/DagHeader.test.tsx
+++ b/airflow-core/src/airflow/ui/src/pages/Dag/DagHeader.test.tsx
@@ -24,7 +24,7 @@ import { afterEach, describe, it, expect, beforeAll, afterAll 
} from "vitest";
 import type { DAGDetailsResponse } from "openapi/requests/types.gen";
 import { handlers } from "src/mocks/handlers";
 import { MOCK_DAG } from "src/mocks/handlers/dag";
-import { BaseWrapper } from "src/utils/Wrapper";
+import { Wrapper } from "src/utils/Wrapper";
 
 import { Header } from "./Header";
 
@@ -41,9 +41,9 @@ afterAll(() => server.close());
 describe("Dag Documentation Modal", () => {
   it("Display documentation button when doc_md is present", async () => {
     render(
-      <BaseWrapper>
+      <Wrapper>
         <Header dag={MOCK_DAG as unknown as DAGDetailsResponse} />
-      </BaseWrapper>,
+      </Wrapper>,
     );
 
     await waitFor(() => 
expect(screen.getByTestId("markdown-button")).toBeInTheDocument());
@@ -55,10 +55,10 @@ describe("Dag Documentation Modal", () => {
 
   it("Do not display documentation button only doc_md is not present", () => {
     render(
-      <BaseWrapper>
+      <Wrapper>
         {/* eslint-disable-next-line unicorn/no-null */}
         <Header dag={{ ...MOCK_DAG, doc_md: null } as unknown as 
DAGDetailsResponse} />
-      </BaseWrapper>,
+      </Wrapper>,
     );
 
     expect(screen.queryByTestId("markdown-button")).toBeNull();
diff --git a/airflow-core/src/airflow/ui/src/pages/DagsList/DagCard.test.tsx 
b/airflow-core/src/airflow/ui/src/pages/DagsList/DagCard.test.tsx
index 03c67ec78d3..0d44614cab2 100644
--- a/airflow-core/src/airflow/ui/src/pages/DagsList/DagCard.test.tsx
+++ b/airflow-core/src/airflow/ui/src/pages/DagsList/DagCard.test.tsx
@@ -102,6 +102,6 @@ describe("DagCard", () => {
 
     render(<DagCard dag={expandedMockDag} />, { wrapper: Wrapper });
     expect(screen.getByTestId("dag-tag")).toBeInTheDocument();
-    expect(screen.getByText(", +2 more")).toBeInTheDocument();
+    expect(screen.getByText("+2 more")).toBeInTheDocument();
   });
 });
diff --git a/airflow-core/src/airflow/ui/src/pages/DagsList/DagTags.tsx 
b/airflow-core/src/airflow/ui/src/pages/DagsList/DagTags.tsx
index c3b42db40f5..f1977bdeec5 100644
--- a/airflow-core/src/airflow/ui/src/pages/DagsList/DagTags.tsx
+++ b/airflow-core/src/airflow/ui/src/pages/DagsList/DagTags.tsx
@@ -17,6 +17,7 @@
  * under the License.
  */
 import { FiTag } from "react-icons/fi";
+import { Link as RouterLink } from "react-router-dom";
 
 import type { DagTagResponse } from "openapi/requests/types.gen";
 import { LimitedItemsList } from "src/components/LimitedItemsList";
@@ -31,7 +32,12 @@ type Props = {
 export const DagTags = ({ hideIcon = false, tags }: Props) => (
   <LimitedItemsList
     icon={hideIcon ? undefined : <FiTag data-testid="dag-tag" />}
-    items={tags.map(({ name }) => name)}
+    interactive
+    items={tags.map(({ name }) => (
+      <RouterLink key={name} to={`/dags?tags=${name}`}>
+        {name}
+      </RouterLink>
+    ))}
     maxItems={MAX_TAGS}
   />
 );

Reply via email to