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}
/>
);