Skip to content

Commit f6141ff

Browse files
authored
Add infinite scroll on tag search (apache#50732)
* Refetch tags on each keypress * Added debouncing * Added infinite scroll * Fixes: reopening filter led to empty list * Undo dagfilters change * redo tag filtering with custom infinite query * Fixes type annotations * Update file name and add license
1 parent 2bd66b5 commit f6141ff

File tree

3 files changed

+83
-4
lines changed

3 files changed

+83
-4
lines changed
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
/*!
2+
* Licensed to the Apache Software Foundation (ASF) under one
3+
* or more contributor license agreements. See the NOTICE file
4+
* distributed with this work for additional information
5+
* regarding copyright ownership. The ASF licenses this file
6+
* to you under the Apache License, Version 2.0 (the
7+
* "License"); you may not use this file except in compliance
8+
* with the License. You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing,
13+
* software distributed under the License is distributed on an
14+
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15+
* KIND, either express or implied. See the License for the
16+
* specific language governing permissions and limitations
17+
* under the License.
18+
*/
19+
import { InfiniteData, useInfiniteQuery, UseInfiniteQueryOptions } from "@tanstack/react-query";
20+
21+
import { DagService } from "openapi/requests/services.gen";
22+
import { DAGTagCollectionResponse } from "openapi/requests/types.gen";
23+
24+
import * as Common from "./common";
25+
26+
export const useDagTagsInfinite = <TError = unknown, TQueryKey extends Array<unknown> = unknown[]>(
27+
{
28+
limit,
29+
orderBy,
30+
tagNamePattern,
31+
}: {
32+
limit?: number;
33+
orderBy?: string;
34+
tagNamePattern?: string;
35+
} = {},
36+
queryKey?: TQueryKey,
37+
options?: Omit<
38+
UseInfiniteQueryOptions<
39+
DAGTagCollectionResponse,
40+
TError,
41+
InfiniteData<DAGTagCollectionResponse>,
42+
DAGTagCollectionResponse,
43+
unknown[],
44+
number
45+
>,
46+
"queryKey" | "queryFn"
47+
>,
48+
) =>
49+
useInfiniteQuery({
50+
queryKey: Common.UseDagServiceGetDagTagsKeyFn({ limit, orderBy, tagNamePattern }, queryKey),
51+
queryFn: ({ pageParam }) => DagService.getDagTags({ limit, offset: pageParam, orderBy, tagNamePattern }),
52+
initialPageParam: 0,
53+
getNextPageParam: (lastPage, _allPages, lastPageParam, _allPageParams) =>
54+
lastPageParam < lastPage.total_entries ? lastPage.tags.length + lastPageParam : undefined,
55+
getPreviousPageParam: (firstPage, _allPages, firstPageParam, _allPageParams) =>
56+
firstPageParam > 0 ? -firstPage.tags.length + firstPageParam : undefined,
57+
...options,
58+
});

airflow-core/src/airflow/ui/src/pages/DagsList/DagsFilters/DagsFilters.tsx

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -18,10 +18,10 @@
1818
*/
1919
import { Box, HStack } from "@chakra-ui/react";
2020
import type { MultiValue } from "chakra-react-select";
21-
import { useCallback } from "react";
21+
import { useCallback, useState } from "react";
2222
import { useSearchParams } from "react-router-dom";
2323

24-
import { useDagServiceGetDagTags } from "openapi/queries";
24+
import { useDagTagsInfinite } from "openapi/queries/useDagsInfinite";
2525
import { useTableURLState } from "src/components/DataTable/useTableUrlState";
2626
import { SearchParamsKeys, type SearchParamsKeysType } from "src/constants/searchParams";
2727
import { useConfig } from "src/queries/useConfig";
@@ -67,8 +67,12 @@ export const DagsFilters = () => {
6767
const isFailed = state === "failed";
6868
const isSuccess = state === "success";
6969

70-
const { data } = useDagServiceGetDagTags({
70+
const [pattern, setPattern] = useState("");
71+
72+
const { data, fetchNextPage, fetchPreviousPage } = useDagTagsInfinite({
73+
limit: 10,
7174
orderBy: "name",
75+
tagNamePattern: pattern,
7276
});
7377

7478
const hidePausedDagsByDefault = Boolean(useConfig("hide_paused_dags_by_default"));
@@ -140,6 +144,7 @@ export const DagsFilters = () => {
140144
searchParams.delete(TAGS_MATCH_MODE_PARAM);
141145

142146
setSearchParams(searchParams);
147+
setPattern("");
143148
};
144149

145150
const handleTagModeChange = useCallback(
@@ -170,11 +175,18 @@ export const DagsFilters = () => {
170175
showPaused={showPaused}
171176
/>
172177
<TagFilter
178+
onMenuScrollToBottom={() => {
179+
void fetchNextPage();
180+
}}
181+
onMenuScrollToTop={() => {
182+
void fetchPreviousPage();
183+
}}
173184
onSelectTagsChange={handleSelectTagsChange}
174185
onTagModeChange={handleTagModeChange}
186+
onUpdate={setPattern}
175187
selectedTags={selectedTags}
176188
tagFilterMode={tagFilterMode}
177-
tags={data?.tags ?? []}
189+
tags={data?.pages.flatMap((dagResponse) => dagResponse.tags) ?? []}
178190
/>
179191
</HStack>
180192
<Box>

airflow-core/src/airflow/ui/src/pages/DagsList/DagsFilters/TagFilter.tsx

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,16 +23,22 @@ import { useTranslation } from "react-i18next";
2323
import { Switch } from "src/components/ui";
2424

2525
type Props = {
26+
readonly onMenuScrollToBottom: () => void;
27+
readonly onMenuScrollToTop: () => void;
2628
readonly onSelectTagsChange: (tags: MultiValue<{ label: string; value: string }>) => void;
2729
readonly onTagModeChange: ({ checked }: { checked: boolean }) => void;
30+
readonly onUpdate: (newValue: string) => void;
2831
readonly selectedTags: Array<string>;
2932
readonly tagFilterMode: string;
3033
readonly tags: Array<string>;
3134
};
3235

3336
export const TagFilter = ({
37+
onMenuScrollToBottom,
38+
onMenuScrollToTop,
3439
onSelectTagsChange,
3540
onTagModeChange,
41+
onUpdate,
3642
selectedTags,
3743
tagFilterMode,
3844
tags,
@@ -66,6 +72,9 @@ export const TagFilter = ({
6672
isMulti
6773
noOptionsMessage={() => translate("table.noTagsFound")}
6874
onChange={onSelectTagsChange}
75+
onInputChange={(newValue) => onUpdate(newValue)}
76+
onMenuScrollToBottom={onMenuScrollToBottom}
77+
onMenuScrollToTop={onMenuScrollToTop}
6978
options={tags.map((tag) => ({
7079
label: tag,
7180
value: tag,

0 commit comments

Comments
 (0)