From 185154d4b90b49d7c2e97bc679b729006535bafb Mon Sep 17 00:00:00 2001 From: Mariana Santana Date: Mon, 17 Mar 2025 08:44:50 +0000 Subject: [PATCH 1/8] Implement multisort in DAGs table Frontend: Implement multisort for DAGs when displayed in a table; The URL displays extra sorting attributes when Shift+click is used on the column to sort by. This implements the UI portion of #46383 --- .../src/airflow/ui/src/components/DataTable/DataTable.tsx | 1 + .../airflow/ui/src/components/DataTable/searchParams.ts | 7 ++----- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/airflow-core/src/airflow/ui/src/components/DataTable/DataTable.tsx b/airflow-core/src/airflow/ui/src/components/DataTable/DataTable.tsx index ff625191d1385..c9d369b62fa0a 100644 --- a/airflow-core/src/airflow/ui/src/components/DataTable/DataTable.tsx +++ b/airflow-core/src/airflow/ui/src/components/DataTable/DataTable.tsx @@ -106,6 +106,7 @@ export const DataTable = ({ columns, data, enableHiding: true, + enableMultiSort: true, getCoreRowModel: getCoreRowModel(), getExpandedRowModel: getExpandedRowModel(), getPaginationRowModel: getPaginationRowModel(), diff --git a/airflow-core/src/airflow/ui/src/components/DataTable/searchParams.ts b/airflow-core/src/airflow/ui/src/components/DataTable/searchParams.ts index f1923e90e2244..4e25b84dab629 100644 --- a/airflow-core/src/airflow/ui/src/components/DataTable/searchParams.ts +++ b/airflow-core/src/airflow/ui/src/components/DataTable/searchParams.ts @@ -40,12 +40,9 @@ export const stateToSearchParams = (state: TableState, defaultTableState?: Table } if (state.sorting.length) { + queryParams.delete(SORT_PARAM); state.sorting.forEach(({ desc, id }) => { - if (defaultTableState?.sorting.find((sort) => sort.id === id && sort.desc === desc)) { - queryParams.delete(SORT_PARAM, `${desc ? "-" : ""}${id}`); - } else { - queryParams.set(SORT_PARAM, `${desc ? "-" : ""}${id}`); - } + queryParams.append(SORT_PARAM, `${desc ? "-" : ""}${id}`); }); } else { queryParams.delete(SORT_PARAM); From ef84bc2cead14b28a3d826c777a076638c19eba5 Mon Sep 17 00:00:00 2001 From: Mariana Santana Date: Fri, 21 Mar 2025 21:38:30 +0000 Subject: [PATCH 2/8] Implement multisort in DAGs table Frontend: Implement multisort for DAGs when displayed in a table; The browser and backend URLs display extra sorting attributes when Shift+click is used on the column to sort by. This implements the UI portion of #46383 --- .../api_fastapi/core_api/openapi/v2-rest-api-generated.yaml | 2 -- airflow-core/src/airflow/ui/src/pages/DagsList/DagsList.tsx | 3 +-- airflow-core/src/airflow/ui/src/pages/DagsList/SortSelect.tsx | 2 +- 3 files changed, 2 insertions(+), 5 deletions(-) diff --git a/airflow-core/src/airflow/api_fastapi/core_api/openapi/v2-rest-api-generated.yaml b/airflow-core/src/airflow/api_fastapi/core_api/openapi/v2-rest-api-generated.yaml index 8961ba7888091..1e7f2f690656c 100644 --- a/airflow-core/src/airflow/api_fastapi/core_api/openapi/v2-rest-api-generated.yaml +++ b/airflow-core/src/airflow/api_fastapi/core_api/openapi/v2-rest-api-generated.yaml @@ -2931,8 +2931,6 @@ paths: type: array items: type: string - default: - - dag_id title: Order By - name: is_favorite in: query diff --git a/airflow-core/src/airflow/ui/src/pages/DagsList/DagsList.tsx b/airflow-core/src/airflow/ui/src/pages/DagsList/DagsList.tsx index f3f785218a579..8e76a5379bc0d 100644 --- a/airflow-core/src/airflow/ui/src/pages/DagsList/DagsList.tsx +++ b/airflow-core/src/airflow/ui/src/pages/DagsList/DagsList.tsx @@ -208,8 +208,7 @@ export const DagsList = () => { searchParams.get(NAME_PATTERN) ?? savedSearchPattern, ); - const [sort] = sorting; - const orderBy = sort ? `${sort.desc ? "-" : ""}${sort.id}` : "dag_display_name"; + const orderBy = new URLSearchParams(globalThis.location.search).getAll("sort"); const columns = useMemo(() => createColumns(translate), [translate]); diff --git a/airflow-core/src/airflow/ui/src/pages/DagsList/SortSelect.tsx b/airflow-core/src/airflow/ui/src/pages/DagsList/SortSelect.tsx index e0e66b9033f96..e5e977f91b9ef 100644 --- a/airflow-core/src/airflow/ui/src/pages/DagsList/SortSelect.tsx +++ b/airflow-core/src/airflow/ui/src/pages/DagsList/SortSelect.tsx @@ -24,7 +24,7 @@ import { createDagSortOptions } from "src/constants/sortParams"; type Props = { readonly handleSortChange: ({ value }: SelectValueChangeDetails>) => void; - readonly orderBy?: string; + readonly orderBy?: string[]; }; export const SortSelect = ({ handleSortChange, orderBy }: Props) => { From 1ff8c2767a71ffdaad96225b74c06145edd4c94d Mon Sep 17 00:00:00 2001 From: Mariana Santana Date: Mon, 7 Apr 2025 23:25:09 +0100 Subject: [PATCH 3/8] Implement multisort in DAGs table Frontend: Implement multisort for DAGs when displayed in a table; Add default to the sorting parameters; Add support for multiple sort parameters in more lists This implements the UI portion of apache#46383 --- .../src/airflow/ui/src/pages/AssetsList/AssetsList.tsx | 3 +-- .../src/airflow/ui/src/pages/Connections/Connections.tsx | 5 +++-- airflow-core/src/airflow/ui/src/pages/DagRuns.tsx | 4 ++-- airflow-core/src/airflow/ui/src/pages/DagsList/DagsList.tsx | 3 ++- airflow-core/src/airflow/ui/src/pages/Events/Events.tsx | 4 ++-- .../src/airflow/ui/src/pages/TaskInstances/TaskInstances.tsx | 4 ++-- .../src/airflow/ui/src/pages/Variables/Variables.tsx | 4 ++-- 7 files changed, 14 insertions(+), 13 deletions(-) diff --git a/airflow-core/src/airflow/ui/src/pages/AssetsList/AssetsList.tsx b/airflow-core/src/airflow/ui/src/pages/AssetsList/AssetsList.tsx index 960a959af65ba..aebdf8f4df210 100644 --- a/airflow-core/src/airflow/ui/src/pages/AssetsList/AssetsList.tsx +++ b/airflow-core/src/airflow/ui/src/pages/AssetsList/AssetsList.tsx @@ -104,8 +104,7 @@ export const AssetsList = () => { const { setTableURLState, tableURLState } = useTableURLState(); const { pagination, sorting } = tableURLState; - const [sort] = sorting; - const orderBy = sort ? [`${sort.desc ? "-" : ""}${sort.id}`] : undefined; + const orderBy = new URLSearchParams(globalThis.location.search).getAll("sort"); const { data, error, isLoading } = useAssetServiceGetAssets({ limit: pagination.pageSize, diff --git a/airflow-core/src/airflow/ui/src/pages/Connections/Connections.tsx b/airflow-core/src/airflow/ui/src/pages/Connections/Connections.tsx index 4ff052ec3a9a3..863c916d95fdf 100644 --- a/airflow-core/src/airflow/ui/src/pages/Connections/Connections.tsx +++ b/airflow-core/src/airflow/ui/src/pages/Connections/Connections.tsx @@ -134,8 +134,9 @@ export const Connections = () => { useConnectionTypeMeta(); // Pre-fetch connection type metadata const { pagination, sorting } = tableURLState; - const [sort] = sorting; - const orderBy = sort ? [`${sort.desc ? "-" : ""}${sort.id}`] : ["connection_id"]; + const orderBy = new URLSearchParams(globalThis.location.search).getAll("sort").length === 0 ? + ["-connection_id"] : new URLSearchParams(globalThis.location.search).getAll("sort"); + const { data, error, isFetching, isLoading } = useConnectionServiceGetConnections({ connectionIdPattern: connectionIdPattern ?? undefined, limit: pagination.pageSize, diff --git a/airflow-core/src/airflow/ui/src/pages/DagRuns.tsx b/airflow-core/src/airflow/ui/src/pages/DagRuns.tsx index c0376ce4b77fd..281481fd043f2 100644 --- a/airflow-core/src/airflow/ui/src/pages/DagRuns.tsx +++ b/airflow-core/src/airflow/ui/src/pages/DagRuns.tsx @@ -174,8 +174,8 @@ export const DagRuns = () => { const { setTableURLState, tableURLState } = useTableURLState(); const { pagination, sorting } = tableURLState; - const [sort] = sorting; - const orderBy = sort ? [`${sort.desc ? "-" : ""}${sort.id}`] : ["-run_after"]; + const orderBy = new URLSearchParams(globalThis.location.search).getAll("sort").length === 0 ? + ["-run_after"] : new URLSearchParams(globalThis.location.search).getAll("sort"); const { pageIndex, pageSize } = pagination; const filteredState = searchParams.get(STATE_PARAM); diff --git a/airflow-core/src/airflow/ui/src/pages/DagsList/DagsList.tsx b/airflow-core/src/airflow/ui/src/pages/DagsList/DagsList.tsx index 8e76a5379bc0d..165041a989cc7 100644 --- a/airflow-core/src/airflow/ui/src/pages/DagsList/DagsList.tsx +++ b/airflow-core/src/airflow/ui/src/pages/DagsList/DagsList.tsx @@ -208,7 +208,8 @@ export const DagsList = () => { searchParams.get(NAME_PATTERN) ?? savedSearchPattern, ); - const orderBy = new URLSearchParams(globalThis.location.search).getAll("sort"); + const orderBy = new URLSearchParams(globalThis.location.search).getAll("sort").length === 0 ? + ["-last_run_start_date"] : new URLSearchParams(globalThis.location.search).getAll("sort"); const columns = useMemo(() => createColumns(translate), [translate]); diff --git a/airflow-core/src/airflow/ui/src/pages/Events/Events.tsx b/airflow-core/src/airflow/ui/src/pages/Events/Events.tsx index d804ee30686e5..855647880b60c 100644 --- a/airflow-core/src/airflow/ui/src/pages/Events/Events.tsx +++ b/airflow-core/src/airflow/ui/src/pages/Events/Events.tsx @@ -146,10 +146,10 @@ export const Events = () => { const { dagId, runId, taskId } = useParams(); const { setTableURLState, tableURLState } = useTableURLState(); const { pagination, sorting } = tableURLState; - const [sort] = sorting; const { onClose, onOpen, open } = useDisclosure(); - const orderBy = sort ? [`${sort.desc ? "-" : ""}${sort.id}`] : ["-when"]; + const orderBy = new URLSearchParams(globalThis.location.search).getAll("sort").length === 0 ? + ["-when"] : new URLSearchParams(globalThis.location.search).getAll("sort"); const { data, error, isFetching, isLoading } = useEventLogServiceGetEventLogs( { diff --git a/airflow-core/src/airflow/ui/src/pages/TaskInstances/TaskInstances.tsx b/airflow-core/src/airflow/ui/src/pages/TaskInstances/TaskInstances.tsx index 01b1e0b5d57fc..66f0154293bd1 100644 --- a/airflow-core/src/airflow/ui/src/pages/TaskInstances/TaskInstances.tsx +++ b/airflow-core/src/airflow/ui/src/pages/TaskInstances/TaskInstances.tsx @@ -191,8 +191,8 @@ export const TaskInstances = () => { const [searchParams] = useSearchParams(); const { setTableURLState, tableURLState } = useTableURLState(); const { pagination, sorting } = tableURLState; - const [sort] = sorting; - const orderBy = sort ? [`${sort.desc ? "-" : ""}${sort.id}`] : ["-start_date", "-run_after"]; + const orderBy = new URLSearchParams(globalThis.location.search).getAll("sort").length === 0 ? + ["-start_date"] : new URLSearchParams(globalThis.location.search).getAll("sort"); const filteredState = searchParams.getAll(STATE_PARAM); const startDate = searchParams.get(START_DATE_PARAM); diff --git a/airflow-core/src/airflow/ui/src/pages/Variables/Variables.tsx b/airflow-core/src/airflow/ui/src/pages/Variables/Variables.tsx index 3e4ccc2927a5c..cd51c8d941a68 100644 --- a/airflow-core/src/airflow/ui/src/pages/Variables/Variables.tsx +++ b/airflow-core/src/airflow/ui/src/pages/Variables/Variables.tsx @@ -123,8 +123,8 @@ export const Variables = () => { ); const [selectedVariables, setSelectedVariables] = useState>({}); const { pagination, sorting } = tableURLState; - const [sort] = sorting; - const orderBy = sort ? [`${sort.desc ? "-" : ""}${sort.id === "value" ? "_val" : sort.id}`] : ["-key"]; + const orderBy = new URLSearchParams(globalThis.location.search).getAll("sort").length === 0 + ? ["-key"] : new URLSearchParams(globalThis.location.search).getAll("sort").map(el => el.replace("value", "_val")); const { data, error, isFetching, isLoading } = useVariableServiceGetVariables({ limit: pagination.pageSize, From 14341ca99b9b7a867b8a55e96a95bf68b58d18ec Mon Sep 17 00:00:00 2001 From: Mariana Santana Date: Fri, 25 Apr 2025 16:44:40 +0100 Subject: [PATCH 4/8] Implement multisort in DAGs table Frontend: Implement multisort for DAGs when displayed in a table; Add function to get default sorting parameters This implements the UI portion of apache#46383 --- .../src/airflow/ui/src/pages/AssetsList/AssetsList.tsx | 4 +++- .../src/airflow/ui/src/pages/Connections/Connections.tsx | 4 ++-- .../src/airflow/ui/src/pages/Dag/Overview/Overview.tsx | 2 +- airflow-core/src/airflow/ui/src/pages/DagRuns.tsx | 5 ++--- airflow-core/src/airflow/ui/src/pages/DagsList/DagsList.tsx | 4 ++-- airflow-core/src/airflow/ui/src/pages/Events/Events.tsx | 4 ++-- .../src/airflow/ui/src/pages/TaskInstances/TaskInstances.tsx | 5 ++--- .../src/airflow/ui/src/pages/Variables/Variables.tsx | 4 ++-- airflow-core/src/airflow/ui/src/utils/query.ts | 5 +++++ 9 files changed, 21 insertions(+), 16 deletions(-) diff --git a/airflow-core/src/airflow/ui/src/pages/AssetsList/AssetsList.tsx b/airflow-core/src/airflow/ui/src/pages/AssetsList/AssetsList.tsx index aebdf8f4df210..3321600e00c50 100644 --- a/airflow-core/src/airflow/ui/src/pages/AssetsList/AssetsList.tsx +++ b/airflow-core/src/airflow/ui/src/pages/AssetsList/AssetsList.tsx @@ -32,6 +32,7 @@ import { SearchBar } from "src/components/SearchBar"; import Time from "src/components/Time"; import { SearchParamsKeys } from "src/constants/searchParams"; import { CreateAssetEvent } from "src/pages/Asset/CreateAssetEvent"; +import { getOrderBy } from "src/utils"; import { DependencyPopover } from "./DependencyPopover"; @@ -104,7 +105,8 @@ export const AssetsList = () => { const { setTableURLState, tableURLState } = useTableURLState(); const { pagination, sorting } = tableURLState; - const orderBy = new URLSearchParams(globalThis.location.search).getAll("sort"); + + const orderBy = getOrderBy(); const { data, error, isLoading } = useAssetServiceGetAssets({ limit: pagination.pageSize, diff --git a/airflow-core/src/airflow/ui/src/pages/Connections/Connections.tsx b/airflow-core/src/airflow/ui/src/pages/Connections/Connections.tsx index 863c916d95fdf..f270be4099c72 100644 --- a/airflow-core/src/airflow/ui/src/pages/Connections/Connections.tsx +++ b/airflow-core/src/airflow/ui/src/pages/Connections/Connections.tsx @@ -35,6 +35,7 @@ import { ActionBar } from "src/components/ui/ActionBar"; import { Checkbox } from "src/components/ui/Checkbox"; import { SearchParamsKeys, type SearchParamsKeysType } from "src/constants/searchParams"; import { useConnectionTypeMeta } from "src/queries/useConnectionTypeMeta"; +import { getOrderBy } from "src/utils"; import AddConnectionButton from "./AddConnectionButton"; import DeleteConnectionButton from "./DeleteConnectionButton"; @@ -134,8 +135,7 @@ export const Connections = () => { useConnectionTypeMeta(); // Pre-fetch connection type metadata const { pagination, sorting } = tableURLState; - const orderBy = new URLSearchParams(globalThis.location.search).getAll("sort").length === 0 ? - ["-connection_id"] : new URLSearchParams(globalThis.location.search).getAll("sort"); + const orderBy = getOrderBy("-connection_id"); const { data, error, isFetching, isLoading } = useConnectionServiceGetConnections({ connectionIdPattern: connectionIdPattern ?? undefined, diff --git a/airflow-core/src/airflow/ui/src/pages/Dag/Overview/Overview.tsx b/airflow-core/src/airflow/ui/src/pages/Dag/Overview/Overview.tsx index 0a0c13c4ef251..67d02c8d6ecf0 100644 --- a/airflow-core/src/airflow/ui/src/pages/Dag/Overview/Overview.tsx +++ b/airflow-core/src/airflow/ui/src/pages/Dag/Overview/Overview.tsx @@ -66,7 +66,7 @@ export const Overview = () => { }); const { data: gridRuns, isLoading: isLoadingRuns } = useGridRuns({ limit }); const { data: assetEventsData, isLoading: isLoadingAssetEvents } = useAssetServiceGetAssetEvents({ - limit, + limit: 6, orderBy: [assetSortBy], sourceDagId: dagId, timestampGte: startDate, diff --git a/airflow-core/src/airflow/ui/src/pages/DagRuns.tsx b/airflow-core/src/airflow/ui/src/pages/DagRuns.tsx index 281481fd043f2..5194e8e583434 100644 --- a/airflow-core/src/airflow/ui/src/pages/DagRuns.tsx +++ b/airflow-core/src/airflow/ui/src/pages/DagRuns.tsx @@ -44,7 +44,7 @@ import { Select } from "src/components/ui"; import { SearchParamsKeys, type SearchParamsKeysType } from "src/constants/searchParams"; import { dagRunTypeOptions, dagRunStateOptions as stateOptions } from "src/constants/stateOptions"; import DeleteRunButton from "src/pages/DeleteRunButton"; -import { renderDuration, useAutoRefresh, isStatePending } from "src/utils"; +import { renderDuration, useAutoRefresh, isStatePending, getOrderBy } from "src/utils"; type DagRunRow = { row: { original: DAGRunResponse } }; const { @@ -174,8 +174,7 @@ export const DagRuns = () => { const { setTableURLState, tableURLState } = useTableURLState(); const { pagination, sorting } = tableURLState; - const orderBy = new URLSearchParams(globalThis.location.search).getAll("sort").length === 0 ? - ["-run_after"] : new URLSearchParams(globalThis.location.search).getAll("sort"); + const orderBy = getOrderBy("-run_after"); const { pageIndex, pageSize } = pagination; const filteredState = searchParams.get(STATE_PARAM); diff --git a/airflow-core/src/airflow/ui/src/pages/DagsList/DagsList.tsx b/airflow-core/src/airflow/ui/src/pages/DagsList/DagsList.tsx index 165041a989cc7..7e70c832cec64 100644 --- a/airflow-core/src/airflow/ui/src/pages/DagsList/DagsList.tsx +++ b/airflow-core/src/airflow/ui/src/pages/DagsList/DagsList.tsx @@ -49,6 +49,7 @@ import { SearchParamsKeys } from "src/constants/searchParams"; import { DagsLayout } from "src/layouts/DagsLayout"; import { useConfig } from "src/queries/useConfig"; import { useDags } from "src/queries/useDags"; +import { getOrderBy } from "src/utils"; import { DAGImportErrors } from "../Dashboard/Stats/DAGImportErrors"; import { DagCard } from "./DagCard"; @@ -208,8 +209,7 @@ export const DagsList = () => { searchParams.get(NAME_PATTERN) ?? savedSearchPattern, ); - const orderBy = new URLSearchParams(globalThis.location.search).getAll("sort").length === 0 ? - ["-last_run_start_date"] : new URLSearchParams(globalThis.location.search).getAll("sort"); + const orderBy = getOrderBy("-last_run_start_date"); const columns = useMemo(() => createColumns(translate), [translate]); diff --git a/airflow-core/src/airflow/ui/src/pages/Events/Events.tsx b/airflow-core/src/airflow/ui/src/pages/Events/Events.tsx index 855647880b60c..6053065af08ae 100644 --- a/airflow-core/src/airflow/ui/src/pages/Events/Events.tsx +++ b/airflow-core/src/airflow/ui/src/pages/Events/Events.tsx @@ -29,6 +29,7 @@ import { useTableURLState } from "src/components/DataTable/useTableUrlState"; import { ErrorAlert } from "src/components/ErrorAlert"; import RenderedJsonField from "src/components/RenderedJsonField"; import Time from "src/components/Time"; +import { getOrderBy } from "src/utils"; type EventsColumn = { dagId?: string; @@ -148,8 +149,7 @@ export const Events = () => { const { pagination, sorting } = tableURLState; const { onClose, onOpen, open } = useDisclosure(); - const orderBy = new URLSearchParams(globalThis.location.search).getAll("sort").length === 0 ? - ["-when"] : new URLSearchParams(globalThis.location.search).getAll("sort"); + const orderBy = getOrderBy("-when"); const { data, error, isFetching, isLoading } = useEventLogServiceGetEventLogs( { diff --git a/airflow-core/src/airflow/ui/src/pages/TaskInstances/TaskInstances.tsx b/airflow-core/src/airflow/ui/src/pages/TaskInstances/TaskInstances.tsx index 66f0154293bd1..5e6bb1b1827ad 100644 --- a/airflow-core/src/airflow/ui/src/pages/TaskInstances/TaskInstances.tsx +++ b/airflow-core/src/airflow/ui/src/pages/TaskInstances/TaskInstances.tsx @@ -35,7 +35,7 @@ import { StateBadge } from "src/components/StateBadge"; import Time from "src/components/Time"; import { TruncatedText } from "src/components/TruncatedText"; import { SearchParamsKeys, type SearchParamsKeysType } from "src/constants/searchParams"; -import { getDuration, useAutoRefresh, isStatePending } from "src/utils"; +import { getDuration, useAutoRefresh, isStatePending, getOrderBy } from "src/utils"; import { getTaskInstanceLink } from "src/utils/links"; import DeleteTaskInstanceButton from "./DeleteTaskInstanceButton"; @@ -191,8 +191,7 @@ export const TaskInstances = () => { const [searchParams] = useSearchParams(); const { setTableURLState, tableURLState } = useTableURLState(); const { pagination, sorting } = tableURLState; - const orderBy = new URLSearchParams(globalThis.location.search).getAll("sort").length === 0 ? - ["-start_date"] : new URLSearchParams(globalThis.location.search).getAll("sort"); + const orderBy = getOrderBy("-start_date"); const filteredState = searchParams.getAll(STATE_PARAM); const startDate = searchParams.get(START_DATE_PARAM); diff --git a/airflow-core/src/airflow/ui/src/pages/Variables/Variables.tsx b/airflow-core/src/airflow/ui/src/pages/Variables/Variables.tsx index cd51c8d941a68..8a890608d2cbb 100644 --- a/airflow-core/src/airflow/ui/src/pages/Variables/Variables.tsx +++ b/airflow-core/src/airflow/ui/src/pages/Variables/Variables.tsx @@ -43,6 +43,7 @@ import ImportVariablesButton from "./ImportVariablesButton"; import AddVariableButton from "./ManageVariable/AddVariableButton"; import DeleteVariableButton from "./ManageVariable/DeleteVariableButton"; import EditVariableButton from "./ManageVariable/EditVariableButton"; +import { getOrderBy } from "src/utils"; const getColumns = ({ allRowsSelected, @@ -123,8 +124,7 @@ export const Variables = () => { ); const [selectedVariables, setSelectedVariables] = useState>({}); const { pagination, sorting } = tableURLState; - const orderBy = new URLSearchParams(globalThis.location.search).getAll("sort").length === 0 - ? ["-key"] : new URLSearchParams(globalThis.location.search).getAll("sort").map(el => el.replace("value", "_val")); + const orderBy = getOrderBy("-key").map(el => el.replace("value", "_val")); const { data, error, isFetching, isLoading } = useVariableServiceGetVariables({ limit: pagination.pageSize, diff --git a/airflow-core/src/airflow/ui/src/utils/query.ts b/airflow-core/src/airflow/ui/src/utils/query.ts index 5becaa0ac29fd..ebfed047397f9 100644 --- a/airflow-core/src/airflow/ui/src/utils/query.ts +++ b/airflow-core/src/airflow/ui/src/utils/query.ts @@ -20,6 +20,11 @@ import { useDagServiceGetDagDetails } from "openapi/queries"; import type { TaskInstanceState } from "openapi/requests/types.gen"; import { useConfig } from "src/queries/useConfig"; +export const getOrderBy = (pre_defined?: string): string[] => { + const sortParam = new URLSearchParams(globalThis.location.search).getAll("sort"); + return (sortParam.length === 0 && pre_defined != undefined) ? [pre_defined] : sortParam; +}; + export const isStatePending = (state?: TaskInstanceState | null) => state === "deferred" || state === "scheduled" || From db3089cca3056dd217ce5071e0a8f7e644c79d14 Mon Sep 17 00:00:00 2001 From: Mariana Santana Date: Sat, 26 Apr 2025 15:47:44 +0100 Subject: [PATCH 5/8] Implement multisort in DAGs table Frontend: Implement multisort for DAGs when displayed in a table; Add support for multiple sort parameters in new lists This implements the UI portion of apache#46383 --- airflow-core/src/airflow/ui/src/pages/Pools/Pools.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/airflow-core/src/airflow/ui/src/pages/Pools/Pools.tsx b/airflow-core/src/airflow/ui/src/pages/Pools/Pools.tsx index 8ab1cbfb493c1..446057832fdda 100644 --- a/airflow-core/src/airflow/ui/src/pages/Pools/Pools.tsx +++ b/airflow-core/src/airflow/ui/src/pages/Pools/Pools.tsx @@ -35,6 +35,7 @@ import { SearchParamsKeys } from "src/constants/searchParams"; import AddPoolButton from "./AddPoolButton"; import PoolBarCard from "./PoolBarCard"; +import { getOrderBy } from "src/utils"; const cardDef = (): CardDef => ({ card: ({ row }) => , @@ -58,8 +59,7 @@ export const Pools = () => { const { setTableURLState, tableURLState } = useTableURLState(); const { pagination, sorting } = tableURLState; - const [sort] = sorting; - const orderBy = sort ? [`${sort.desc ? "-" : ""}${sort.id}`] : ["name"]; + const orderBy = getOrderBy("name"); const { data, error, isLoading } = usePoolServiceGetPools({ limit: pagination.pageSize, From b9d4808c0885068089bf4b5f1608340d699b8485 Mon Sep 17 00:00:00 2001 From: Mariana Santana Date: Tue, 29 Apr 2025 22:40:13 +0100 Subject: [PATCH 6/8] Implement multisort in DAGs table Frontend: Implement multisort for DAGs when displayed in a table; Fix casing and formatting This implements the UI portion of apache#46383 --- .../src/airflow/ui/src/pages/AssetsList/AssetsList.tsx | 2 +- .../src/airflow/ui/src/pages/Connections/Connections.tsx | 2 +- .../src/airflow/ui/src/pages/DagsList/SortSelect.tsx | 2 +- airflow-core/src/airflow/ui/src/pages/Pools/Pools.tsx | 2 +- .../src/airflow/ui/src/pages/Variables/Variables.tsx | 4 ++-- airflow-core/src/airflow/ui/src/utils/query.ts | 5 +++-- 6 files changed, 9 insertions(+), 8 deletions(-) diff --git a/airflow-core/src/airflow/ui/src/pages/AssetsList/AssetsList.tsx b/airflow-core/src/airflow/ui/src/pages/AssetsList/AssetsList.tsx index 3321600e00c50..1632eb854a20c 100644 --- a/airflow-core/src/airflow/ui/src/pages/AssetsList/AssetsList.tsx +++ b/airflow-core/src/airflow/ui/src/pages/AssetsList/AssetsList.tsx @@ -105,7 +105,7 @@ export const AssetsList = () => { const { setTableURLState, tableURLState } = useTableURLState(); const { pagination, sorting } = tableURLState; - + const orderBy = getOrderBy(); const { data, error, isLoading } = useAssetServiceGetAssets({ diff --git a/airflow-core/src/airflow/ui/src/pages/Connections/Connections.tsx b/airflow-core/src/airflow/ui/src/pages/Connections/Connections.tsx index f270be4099c72..a8d7fd0978384 100644 --- a/airflow-core/src/airflow/ui/src/pages/Connections/Connections.tsx +++ b/airflow-core/src/airflow/ui/src/pages/Connections/Connections.tsx @@ -136,7 +136,7 @@ export const Connections = () => { useConnectionTypeMeta(); // Pre-fetch connection type metadata const { pagination, sorting } = tableURLState; const orderBy = getOrderBy("-connection_id"); - + const { data, error, isFetching, isLoading } = useConnectionServiceGetConnections({ connectionIdPattern: connectionIdPattern ?? undefined, limit: pagination.pageSize, diff --git a/airflow-core/src/airflow/ui/src/pages/DagsList/SortSelect.tsx b/airflow-core/src/airflow/ui/src/pages/DagsList/SortSelect.tsx index e5e977f91b9ef..bf38f68b7d2e9 100644 --- a/airflow-core/src/airflow/ui/src/pages/DagsList/SortSelect.tsx +++ b/airflow-core/src/airflow/ui/src/pages/DagsList/SortSelect.tsx @@ -24,7 +24,7 @@ import { createDagSortOptions } from "src/constants/sortParams"; type Props = { readonly handleSortChange: ({ value }: SelectValueChangeDetails>) => void; - readonly orderBy?: string[]; + readonly orderBy?: Array; }; export const SortSelect = ({ handleSortChange, orderBy }: Props) => { diff --git a/airflow-core/src/airflow/ui/src/pages/Pools/Pools.tsx b/airflow-core/src/airflow/ui/src/pages/Pools/Pools.tsx index 446057832fdda..99493da28b36f 100644 --- a/airflow-core/src/airflow/ui/src/pages/Pools/Pools.tsx +++ b/airflow-core/src/airflow/ui/src/pages/Pools/Pools.tsx @@ -32,10 +32,10 @@ import { SearchBar } from "src/components/SearchBar"; import { Select } from "src/components/ui"; import type { SearchParamsKeysType } from "src/constants/searchParams"; import { SearchParamsKeys } from "src/constants/searchParams"; +import { getOrderBy } from "src/utils"; import AddPoolButton from "./AddPoolButton"; import PoolBarCard from "./PoolBarCard"; -import { getOrderBy } from "src/utils"; const cardDef = (): CardDef => ({ card: ({ row }) => , diff --git a/airflow-core/src/airflow/ui/src/pages/Variables/Variables.tsx b/airflow-core/src/airflow/ui/src/pages/Variables/Variables.tsx index 8a890608d2cbb..06ce4bce2770d 100644 --- a/airflow-core/src/airflow/ui/src/pages/Variables/Variables.tsx +++ b/airflow-core/src/airflow/ui/src/pages/Variables/Variables.tsx @@ -35,6 +35,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 { getOrderBy } from "src/utils"; import { TrimText } from "src/utils/TrimText"; import { downloadJson } from "src/utils/downloadJson"; @@ -43,7 +44,6 @@ import ImportVariablesButton from "./ImportVariablesButton"; import AddVariableButton from "./ManageVariable/AddVariableButton"; import DeleteVariableButton from "./ManageVariable/DeleteVariableButton"; import EditVariableButton from "./ManageVariable/EditVariableButton"; -import { getOrderBy } from "src/utils"; const getColumns = ({ allRowsSelected, @@ -124,7 +124,7 @@ export const Variables = () => { ); const [selectedVariables, setSelectedVariables] = useState>({}); const { pagination, sorting } = tableURLState; - const orderBy = getOrderBy("-key").map(el => el.replace("value", "_val")); + const orderBy = getOrderBy("-key").map((el) => el.replace("value", "_val")); const { data, error, isFetching, isLoading } = useVariableServiceGetVariables({ limit: pagination.pageSize, diff --git a/airflow-core/src/airflow/ui/src/utils/query.ts b/airflow-core/src/airflow/ui/src/utils/query.ts index ebfed047397f9..edb6ce4ea0d9a 100644 --- a/airflow-core/src/airflow/ui/src/utils/query.ts +++ b/airflow-core/src/airflow/ui/src/utils/query.ts @@ -20,9 +20,10 @@ import { useDagServiceGetDagDetails } from "openapi/queries"; import type { TaskInstanceState } from "openapi/requests/types.gen"; import { useConfig } from "src/queries/useConfig"; -export const getOrderBy = (pre_defined?: string): string[] => { +export const getOrderBy = (defaultOrder?: string): Array => { const sortParam = new URLSearchParams(globalThis.location.search).getAll("sort"); - return (sortParam.length === 0 && pre_defined != undefined) ? [pre_defined] : sortParam; + + return sortParam.length === 0 && defaultOrder !== undefined ? [defaultOrder] : sortParam; }; export const isStatePending = (state?: TaskInstanceState | null) => From fdcb181c03c1a257c4b94440a6c43dac9fe01580 Mon Sep 17 00:00:00 2001 From: Mariana Santana Date: Mon, 4 Aug 2025 22:04:50 +0100 Subject: [PATCH 7/8] minor fixes --- airflow-core/src/airflow/ui/src/pages/DagsList/DagsList.tsx | 2 +- airflow-core/src/airflow/ui/src/pages/DagsList/SortSelect.tsx | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/airflow-core/src/airflow/ui/src/pages/DagsList/DagsList.tsx b/airflow-core/src/airflow/ui/src/pages/DagsList/DagsList.tsx index 7e70c832cec64..4153e47397d15 100644 --- a/airflow-core/src/airflow/ui/src/pages/DagsList/DagsList.tsx +++ b/airflow-core/src/airflow/ui/src/pages/DagsList/DagsList.tsx @@ -252,7 +252,7 @@ export const DagsList = () => { lastDagRunState, limit: pagination.pageSize, offset: pagination.pageIndex * pagination.pageSize, - orderBy: [orderBy], + orderBy: orderBy, owners, paused, tags: selectedTags, diff --git a/airflow-core/src/airflow/ui/src/pages/DagsList/SortSelect.tsx b/airflow-core/src/airflow/ui/src/pages/DagsList/SortSelect.tsx index bf38f68b7d2e9..f5a7d68dab424 100644 --- a/airflow-core/src/airflow/ui/src/pages/DagsList/SortSelect.tsx +++ b/airflow-core/src/airflow/ui/src/pages/DagsList/SortSelect.tsx @@ -36,7 +36,7 @@ export const SortSelect = ({ handleSortChange, orderBy }: Props) => { collection={dagSortOptions} data-testid="sort-by-select" onValueChange={handleSortChange} - value={orderBy === undefined ? undefined : [orderBy]} + value={orderBy === undefined ? undefined : orderBy} width="310px" > From 7dbc1d55a722fc8fade05ed1038c7090f2b6fe04 Mon Sep 17 00:00:00 2001 From: Mariana Santana Date: Mon, 4 Aug 2025 22:10:33 +0100 Subject: [PATCH 8/8] correction for assetlayout feature --- airflow-core/src/airflow/ui/src/pages/Asset/AssetLayout.tsx | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/airflow-core/src/airflow/ui/src/pages/Asset/AssetLayout.tsx b/airflow-core/src/airflow/ui/src/pages/Asset/AssetLayout.tsx index 51a1442fcc90b..b398b429ee4b6 100644 --- a/airflow-core/src/airflow/ui/src/pages/Asset/AssetLayout.tsx +++ b/airflow-core/src/airflow/ui/src/pages/Asset/AssetLayout.tsx @@ -32,6 +32,7 @@ import { ProgressBar } from "src/components/ui"; import { AssetGraph } from "./AssetGraph"; import { CreateAssetEvent } from "./CreateAssetEvent"; import { Header } from "./Header"; +import { getOrderBy } from "src/utils"; export const AssetLayout = () => { const { i18n, t: translate } = useTranslation(["assets", "common"]); @@ -40,8 +41,8 @@ export const AssetLayout = () => { const { setTableURLState, tableURLState } = useTableURLState(); const { pagination, sorting } = tableURLState; - const [sort] = sorting; - const orderBy = sort ? [`${sort.desc ? "-" : ""}${sort.id}`] : ["-timestamp"]; + + const orderBy = getOrderBy("-timestamp"); const { data: asset, isLoading } = useAssetServiceGetAsset( { assetId: assetId === undefined ? 0 : parseInt(assetId, 10) },