This is an automated email from the ASF dual-hosted git repository.
ephraimanierobi pushed a commit to branch v3-1-test
in repository https://gitbox.apache.org/repos/asf/airflow.git
commit 507db91c6f8924af95f9b5114f24b0ca88bdad69
Author: github-actions[bot]
<41898282+github-actions[bot]@users.noreply.github.com>
AuthorDate: Fri Jan 9 11:11:42 2026 -0500
[v3-1-test] Move dags list filters to buttongroups (#60298) (#60337)
(cherry picked from commit cd3b6b4578792b4fb172ad22b101da85a51eb3aa)
Co-authored-by: Brent Bovenzi <[email protected]>
---
.../components/DataTable/ToggleTableDisplay.tsx | 58 +++++++------------
.../src/pages/DagsList/DagsFilters/DagsFilters.tsx | 60 ++++++++++----------
.../pages/DagsList/DagsFilters/FavoriteFilter.tsx | 66 +++++++++++++---------
.../pages/DagsList/DagsFilters/PausedFilter.tsx | 59 ++++++++++---------
.../pages/DagsList/DagsFilters/StateFilters.tsx | 57 ++++++++++++-------
.../src/pages/DagsList/DagsFilters/TagFilter.tsx | 1 +
6 files changed, 158 insertions(+), 143 deletions(-)
diff --git
a/airflow-core/src/airflow/ui/src/components/DataTable/ToggleTableDisplay.tsx
b/airflow-core/src/airflow/ui/src/components/DataTable/ToggleTableDisplay.tsx
index feb072f8677..75e336d615f 100644
---
a/airflow-core/src/airflow/ui/src/components/DataTable/ToggleTableDisplay.tsx
+++
b/airflow-core/src/airflow/ui/src/components/DataTable/ToggleTableDisplay.tsx
@@ -16,12 +16,10 @@
* specific language governing permissions and limitations
* under the License.
*/
-import { HStack, IconButton } from "@chakra-ui/react";
+import { ButtonGroup, IconButton } from "@chakra-ui/react";
import { useTranslation } from "react-i18next";
import { FiAlignJustify, FiGrid } from "react-icons/fi";
-import { Tooltip } from "src/components/ui";
-
type Display = "card" | "table";
type Props = {
@@ -33,39 +31,25 @@ export const ToggleTableDisplay = ({ display, setDisplay }:
Props) => {
const { t: translate } = useTranslation("components");
return (
- <HStack colorPalette="brand" gap={1} pb={2}>
- <Tooltip content={translate("toggleCardView")}>
- <IconButton
- _hover={{ bg: "colorPalette.emphasized" }}
- aria-label={translate("toggleCardView")}
- bg={display === "card" ? "colorPalette.muted" : "bg"}
- borderColor="border.emphasized"
- borderWidth={1}
- color="colorPalette.fg"
- height={8}
- minWidth={8}
- onClick={() => setDisplay("card")}
- width={8}
- >
- <FiGrid />
- </IconButton>
- </Tooltip>
- <Tooltip content={translate("toggleTableView")}>
- <IconButton
- _hover={{ bg: "colorPalette.emphasized" }}
- aria-label={translate("toggleTableView")}
- bg={display === "table" ? "colorPalette.muted" : "bg"}
- borderColor="border.emphasized"
- borderWidth={1}
- color="colorPalette.fg"
- height={8}
- minWidth={8}
- onClick={() => setDisplay("table")}
- width={8}
- >
- <FiAlignJustify />
- </IconButton>
- </Tooltip>
- </HStack>
+ <ButtonGroup attached colorPalette="brand" pb={2} size="sm"
variant="outline">
+ <IconButton
+ aria-label={translate("toggleCardView")}
+ bg={display === "card" ? "colorPalette.muted" : undefined}
+ onClick={() => setDisplay("card")}
+ title={translate("toggleCardView")}
+ variant={display === "card" ? "solid" : "outline"}
+ >
+ <FiGrid />
+ </IconButton>
+ <IconButton
+ aria-label={translate("toggleTableView")}
+ bg={display === "table" ? "colorPalette.muted" : undefined}
+ onClick={() => setDisplay("table")}
+ title={translate("toggleTableView")}
+ variant={display === "table" ? "solid" : "outline"}
+ >
+ <FiAlignJustify />
+ </IconButton>
+ </ButtonGroup>
);
};
diff --git
a/airflow-core/src/airflow/ui/src/pages/DagsList/DagsFilters/DagsFilters.tsx
b/airflow-core/src/airflow/ui/src/pages/DagsList/DagsFilters/DagsFilters.tsx
index 5b413ab012c..11269d577e9 100644
--- a/airflow-core/src/airflow/ui/src/pages/DagsList/DagsFilters/DagsFilters.tsx
+++ b/airflow-core/src/airflow/ui/src/pages/DagsList/DagsFilters/DagsFilters.tsx
@@ -16,7 +16,7 @@
* specific language governing permissions and limitations
* under the License.
*/
-import { Box, HStack } from "@chakra-ui/react";
+import { Box, Flex } from "@chakra-ui/react";
import type { MultiValue } from "chakra-react-select";
import { useCallback, useState } from "react";
import { useSearchParams } from "react-router-dom";
@@ -72,14 +72,12 @@ export const DagsFilters = () => {
const { setTableURLState, tableURLState } = useTableURLState();
const { pagination, sorting } = tableURLState;
- const handlePausedChange = useCallback(
- ({ value }: { value: Array<string> }) => {
- const [val] = value;
-
- if (val === undefined) {
+ const handlePausedChange: React.MouseEventHandler<HTMLButtonElement> =
useCallback(
+ ({ currentTarget: { value } }) => {
+ if (value === "all") {
searchParams.delete(PAUSED_PARAM);
} else {
- searchParams.set(PAUSED_PARAM, val);
+ searchParams.set(PAUSED_PARAM, value);
}
setTableURLState({
pagination: { ...pagination, pageIndex: 0 },
@@ -91,14 +89,12 @@ export const DagsFilters = () => {
[pagination, searchParams, setSearchParams, setTableURLState, sorting],
);
- const handleFavoriteChange = useCallback(
- ({ value }: { value: Array<string> }) => {
- const [val] = value;
-
- if (val === undefined || val === "all") {
+ const handleFavoriteChange: React.MouseEventHandler<HTMLButtonElement> =
useCallback(
+ ({ currentTarget: { value } }) => {
+ if (value === "all") {
searchParams.delete(FAVORITE_PARAM);
} else {
- searchParams.set(FAVORITE_PARAM, val);
+ searchParams.set(FAVORITE_PARAM, value);
}
setTableURLState({
pagination: { ...pagination, pageIndex: 0 },
@@ -189,8 +185,8 @@ export const DagsFilters = () => {
});
return (
- <HStack justifyContent="space-between">
- <HStack gap={4}>
+ <Flex flexWrap="wrap" gap={4} justifyContent="space-between">
+ <Flex alignItems="center" flexWrap="wrap" gap={4}>
<StateFilters
isAll={isAll}
isFailed={isFailed}
@@ -205,25 +201,27 @@ export const DagsFilters = () => {
onPausedChange={handlePausedChange}
showPaused={showPaused}
/>
- <TagFilter
- onMenuScrollToBottom={() => {
- void fetchNextPage();
- }}
- onMenuScrollToTop={() => {
- void fetchPreviousPage();
- }}
- onSelectTagsChange={handleSelectTagsChange}
- onTagModeChange={handleTagModeChange}
- onUpdate={setPattern}
- selectedTags={selectedTags}
- tagFilterMode={tagFilterMode}
- tags={data?.pages.flatMap((dagResponse) => dagResponse.tags) ?? []}
- />
+ <Box maxWidth="300px">
+ <TagFilter
+ onMenuScrollToBottom={() => {
+ void fetchNextPage();
+ }}
+ onMenuScrollToTop={() => {
+ void fetchPreviousPage();
+ }}
+ onSelectTagsChange={handleSelectTagsChange}
+ onTagModeChange={handleTagModeChange}
+ onUpdate={setPattern}
+ selectedTags={selectedTags}
+ tagFilterMode={tagFilterMode}
+ tags={data?.pages.flatMap((dagResponse) => dagResponse.tags) ?? []}
+ />
+ </Box>
<FavoriteFilter onFavoriteChange={handleFavoriteChange}
showFavorites={showFavorites} />
- </HStack>
+ </Flex>
<Box>
<ResetButton filterCount={filterCount} onClearFilters={onClearFilters}
/>
</Box>
- </HStack>
+ </Flex>
);
};
diff --git
a/airflow-core/src/airflow/ui/src/pages/DagsList/DagsFilters/FavoriteFilter.tsx
b/airflow-core/src/airflow/ui/src/pages/DagsList/DagsFilters/FavoriteFilter.tsx
index 5e8bcd93cd9..3554658f98d 100644
---
a/airflow-core/src/airflow/ui/src/pages/DagsList/DagsFilters/FavoriteFilter.tsx
+++
b/airflow-core/src/airflow/ui/src/pages/DagsList/DagsFilters/FavoriteFilter.tsx
@@ -16,43 +16,55 @@
* specific language governing permissions and limitations
* under the License.
*/
-import { createListCollection, type SelectValueChangeDetails } from
"@chakra-ui/react";
+import { Button, ButtonGroup, Icon } from "@chakra-ui/react";
import { useTranslation } from "react-i18next";
-
-import { Select } from "src/components/ui";
+import { FiStar } from "react-icons/fi";
type Props = {
- readonly onFavoriteChange: (details: SelectValueChangeDetails<string>) =>
void;
+ readonly onFavoriteChange: React.MouseEventHandler<HTMLButtonElement>;
readonly showFavorites: string | null;
};
export const FavoriteFilter = ({ onFavoriteChange, showFavorites }: Props) => {
const { t: translate } = useTranslation("dags");
- const enabledOptions = createListCollection({
- items: [
- { label: translate("filters.favorite.all"), value: "all" },
- { label: translate("filters.favorite.favorite"), value: "true" },
- { label: translate("filters.favorite.unfavorite"), value: "false" },
- ],
- });
+ const currentValue = showFavorites ?? "all";
return (
- <Select.Root
- collection={enabledOptions}
- onValueChange={onFavoriteChange}
- value={[showFavorites ?? "all"]}
- >
- <Select.Trigger colorPalette="brand" isActive={Boolean(showFavorites)}>
- <Select.ValueText width={20} />
- </Select.Trigger>
- <Select.Content>
- {enabledOptions.items.map((option) => (
- <Select.Item item={option} key={option.label}>
- {option.label}
- </Select.Item>
- ))}
- </Select.Content>
- </Select.Root>
+ <ButtonGroup attached size="sm" variant="outline">
+ <Button
+ bg={currentValue === "all" ? "colorPalette.muted" : undefined}
+ colorPalette="brand"
+ onClick={onFavoriteChange}
+ value="all"
+ variant={currentValue === "all" ? "solid" : "outline"}
+ >
+ {translate("filters.favorite.all")}
+ </Button>
+ <Button
+ bg={currentValue === "true" ? "colorPalette.muted" : undefined}
+ colorPalette="brand"
+ onClick={onFavoriteChange}
+ value="true"
+ variant={currentValue === "true" ? "solid" : "outline"}
+ >
+ <Icon asChild color="brand.solid">
+ <FiStar style={{ fill: "currentColor" }} />
+ </Icon>
+ {translate("filters.favorite.favorite")}
+ </Button>
+ <Button
+ bg={currentValue === "false" ? "colorPalette.muted" : undefined}
+ colorPalette="brand"
+ onClick={onFavoriteChange}
+ value="false"
+ variant={currentValue === "false" ? "solid" : "outline"}
+ >
+ <Icon asChild color="fg.muted">
+ <FiStar />
+ </Icon>
+ {translate("filters.favorite.unfavorite")}
+ </Button>
+ </ButtonGroup>
);
};
diff --git
a/airflow-core/src/airflow/ui/src/pages/DagsList/DagsFilters/PausedFilter.tsx
b/airflow-core/src/airflow/ui/src/pages/DagsList/DagsFilters/PausedFilter.tsx
index 042795b18d0..ec21d605109 100644
---
a/airflow-core/src/airflow/ui/src/pages/DagsList/DagsFilters/PausedFilter.tsx
+++
b/airflow-core/src/airflow/ui/src/pages/DagsList/DagsFilters/PausedFilter.tsx
@@ -16,44 +16,49 @@
* specific language governing permissions and limitations
* under the License.
*/
-import { createListCollection, type SelectValueChangeDetails } from
"@chakra-ui/react";
+import { Button, ButtonGroup } from "@chakra-ui/react";
import { useTranslation } from "react-i18next";
-import { Select } from "src/components/ui";
-
type Props = {
readonly defaultShowPaused: string;
- readonly onPausedChange: (details: SelectValueChangeDetails<string>) => void;
+ readonly onPausedChange: React.MouseEventHandler<HTMLButtonElement>;
readonly showPaused: string | null;
};
export const PausedFilter = ({ defaultShowPaused, onPausedChange, showPaused
}: Props) => {
const { t: translate } = useTranslation("dags");
- const enabledOptions = createListCollection({
- items: [
- { label: translate("filters.paused.all"), value: "all" },
- { label: translate("filters.paused.active"), value: "false" },
- { label: translate("filters.paused.paused"), value: "true" },
- ],
- });
+ const currentValue = showPaused ?? defaultShowPaused;
return (
- <Select.Root
- collection={enabledOptions}
- onValueChange={onPausedChange}
- value={[showPaused ?? defaultShowPaused]}
- >
- <Select.Trigger colorPalette="brand" isActive={Boolean(showPaused)}>
- <Select.ValueText width={20} />
- </Select.Trigger>
- <Select.Content>
- {enabledOptions.items.map((option) => (
- <Select.Item item={option} key={option.label}>
- {option.label}
- </Select.Item>
- ))}
- </Select.Content>
- </Select.Root>
+ <ButtonGroup attached size="sm" variant="outline">
+ <Button
+ bg={currentValue === "all" ? "colorPalette.muted" : undefined}
+ colorPalette="brand"
+ onClick={onPausedChange}
+ value="all"
+ variant={currentValue === "all" ? "solid" : "outline"}
+ >
+ {translate("filters.paused.all")}
+ </Button>
+ <Button
+ bg={currentValue === "false" ? "colorPalette.muted" : undefined}
+ colorPalette="brand"
+ onClick={onPausedChange}
+ value="false"
+ variant={currentValue === "false" ? "solid" : "outline"}
+ >
+ {translate("filters.paused.active")}
+ </Button>
+ <Button
+ bg={currentValue === "true" ? "colorPalette.muted" : undefined}
+ colorPalette="brand"
+ onClick={onPausedChange}
+ value="true"
+ variant={currentValue === "true" ? "solid" : "outline"}
+ >
+ {translate("filters.paused.paused")}
+ </Button>
+ </ButtonGroup>
);
};
diff --git
a/airflow-core/src/airflow/ui/src/pages/DagsList/DagsFilters/StateFilters.tsx
b/airflow-core/src/airflow/ui/src/pages/DagsList/DagsFilters/StateFilters.tsx
index 3bda2ba98fe..0533de4dc3a 100644
---
a/airflow-core/src/airflow/ui/src/pages/DagsList/DagsFilters/StateFilters.tsx
+++
b/airflow-core/src/airflow/ui/src/pages/DagsList/DagsFilters/StateFilters.tsx
@@ -16,11 +16,10 @@
* specific language governing permissions and limitations
* under the License.
*/
-import { HStack } from "@chakra-ui/react";
+import { Button, ButtonGroup } from "@chakra-ui/react";
import { useTranslation } from "react-i18next";
import { LuUserRoundPen } from "react-icons/lu";
-import { QuickFilterButton } from "src/components/QuickFilterButton";
import { StateBadge } from "src/components/StateBadge";
type Props = {
@@ -45,57 +44,73 @@ export const StateFilters = ({
const { t: translate } = useTranslation(["dags", "common", "hitl"]);
return (
- <HStack>
- <QuickFilterButton isActive={isAll} onClick={onStateChange} value="all">
+ <ButtonGroup attached size="sm" variant="outline">
+ <Button
+ bg={isAll ? "colorPalette.muted" : undefined}
+ colorPalette="brand"
+ onClick={onStateChange}
+ value="all"
+ variant={isAll ? "solid" : "outline"}
+ >
{translate("dags:filters.paused.all")}
- </QuickFilterButton>
- <QuickFilterButton
+ </Button>
+ <Button
+ bg={isFailed ? "colorPalette.muted" : undefined}
+ colorPalette="brand"
data-testid="dags-failed-filter"
- isActive={isFailed}
onClick={onStateChange}
value="failed"
+ variant={isFailed ? "solid" : "outline"}
>
<StateBadge state="failed" />
{translate("common:states.failed")}
- </QuickFilterButton>
- <QuickFilterButton
+ </Button>
+ <Button
+ bg={isQueued ? "colorPalette.muted" : undefined}
+ colorPalette="brand"
data-testid="dags-queued-filter"
- isActive={isQueued}
onClick={onStateChange}
value="queued"
+ variant={isQueued ? "solid" : "outline"}
>
<StateBadge state="queued" />
{translate("common:states.queued")}
- </QuickFilterButton>
- <QuickFilterButton
+ </Button>
+ <Button
+ bg={isRunning ? "colorPalette.muted" : undefined}
+ colorPalette="brand"
data-testid="dags-running-filter"
- isActive={isRunning}
onClick={onStateChange}
value="running"
+ variant={isRunning ? "solid" : "outline"}
>
<StateBadge state="running" />
{translate("common:states.running")}
- </QuickFilterButton>
- <QuickFilterButton
+ </Button>
+ <Button
+ bg={isSuccess ? "colorPalette.muted" : undefined}
+ colorPalette="brand"
data-testid="dags-success-filter"
- isActive={isSuccess}
onClick={onStateChange}
value="success"
+ variant={isSuccess ? "solid" : "outline"}
>
<StateBadge state="success" />
{translate("common:states.success")}
- </QuickFilterButton>
- <QuickFilterButton
+ </Button>
+ <Button
+ bg={needsReview ? "colorPalette.muted" : undefined}
+ colorPalette="brand"
data-testid="dags-needs-review-filter"
- isActive={needsReview}
onClick={onStateChange}
value="needs_review"
+ variant={needsReview ? "solid" : "outline"}
>
<StateBadge colorPalette="deferred">
<LuUserRoundPen />
</StateBadge>
{translate("hitl:requiredAction_other")}
- </QuickFilterButton>
- </HStack>
+ </Button>
+ </ButtonGroup>
);
};
diff --git
a/airflow-core/src/airflow/ui/src/pages/DagsList/DagsFilters/TagFilter.tsx
b/airflow-core/src/airflow/ui/src/pages/DagsList/DagsFilters/TagFilter.tsx
index 113d7e8de6a..001cd0b94f8 100644
--- a/airflow-core/src/airflow/ui/src/pages/DagsList/DagsFilters/TagFilter.tsx
+++ b/airflow-core/src/airflow/ui/src/pages/DagsList/DagsFilters/TagFilter.tsx
@@ -57,6 +57,7 @@ export const TagFilter = ({
}),
container: (provided) => ({
...provided,
+ maxWidth: 300,
minWidth: 64,
}),
control: (provided) => ({