Skip to content

Commit

Permalink
Improve ability to see and clear dags list filters (apache#43981)
Browse files Browse the repository at this point in the history
* Improve ability to see and clear dags list filters

* Refine filter buttons again
  • Loading branch information
bbovenzi authored Nov 14, 2024
1 parent f60886c commit 61076f0
Show file tree
Hide file tree
Showing 9 changed files with 168 additions and 55 deletions.
12 changes: 4 additions & 8 deletions airflow/ui/src/components/DagRunInfo.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,14 +16,16 @@
* specific language governing permissions and limitations
* under the License.
*/
import { VStack, Text, Box, HStack } from "@chakra-ui/react";
import { VStack, Text, HStack } from "@chakra-ui/react";
import dayjs from "dayjs";

import type { DAGRunResponse } from "openapi/requests/types.gen";
import Time from "src/components/Time";
import { Tooltip } from "src/components/ui";
import { stateColor } from "src/utils/stateColor";

import { StateCircle } from "./StateCircle";

type Props = {
readonly dataIntervalEnd?: string | null;
readonly dataIntervalStart?: string | null;
Expand Down Expand Up @@ -81,13 +83,7 @@ const DagRunInfo = ({
<Time datetime={dataIntervalStart} showTooltip={false} />
{state === undefined ? undefined : (
<>
<Box
bg={stateColor[state]}
borderRadius="50%"
height={2}
minW={2}
width={2}
/>
<StateCircle state={state} />
<Text color={stateColor[state]}>{state}</Text>
</>
)}
Expand Down
12 changes: 10 additions & 2 deletions airflow/ui/src/components/DataTable/ToggleTableDisplay.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -29,21 +29,29 @@ type Props = {
export const ToggleTableDisplay = ({ display, setDisplay }: Props) => (
<HStack colorPalette="blue" gap={1} pb={2}>
<IconButton
_hover={{ bg: "colorPalette.subtle" }}
aria-label="Show card view"
bg={display === "card" ? "colorPalette.muted" : "bg"}
borderColor="colorPalette.fg"
borderWidth={1}
color="colorPalette.fg"
height={8}
minWidth={8}
onClick={() => setDisplay("card")}
variant={display === "table" ? "outline" : "solid"}
width={8}
>
<FiGrid />
</IconButton>
<IconButton
_hover={{ bg: "colorPalette.subtle" }}
aria-label="Show table view"
bg={display === "table" ? "colorPalette.muted" : "bg"}
borderColor="colorPalette.fg"
borderWidth={1}
color="colorPalette.fg"
height={8}
minWidth={8}
onClick={() => setDisplay("table")}
variant={display === "card" ? "outline" : "solid"}
width={8}
>
<FiAlignJustify />
Expand Down
12 changes: 9 additions & 3 deletions airflow/ui/src/components/QuickFilterButton.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,19 +19,25 @@
import { Button, type ButtonProps } from "@chakra-ui/react";

type QuickFilterButtonProps = {
readonly active: boolean;
readonly isActive: boolean;
} & ButtonProps;

export const QuickFilterButton = ({
active,
children,
isActive,
...rest
}: QuickFilterButtonProps) => (
<Button
_hover={{ bg: "colorPalette.subtle" }}
bg={isActive ? "colorPalette.muted" : undefined}
borderColor="colorPalette.fg"
borderRadius={20}
borderWidth={1}
color="colorPalette.fg"
colorPalette="blue"
fontWeight="normal"
variant={active ? "solid" : "outline"}
size="sm"
variant={isActive ? "solid" : "outline"}
{...rest}
>
{children}
Expand Down
3 changes: 2 additions & 1 deletion airflow/ui/src/components/SearchBar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -53,11 +53,13 @@ export const SearchBar = ({
return (
<InputGroup
{...groupProps}
colorPalette="blue"
endElement={
<>
{Boolean(value) ? (
<CloseButton
aria-label="Clear search"
colorPalette="gray"
data-testid="clear-search"
onClick={() => {
setValue("");
Expand All @@ -67,7 +69,6 @@ export const SearchBar = ({
/>
) : undefined}
<Button
colorPalette="blue"
fontWeight="normal"
height="1.75rem"
variant="ghost"
Expand Down
38 changes: 38 additions & 0 deletions airflow/ui/src/components/StateCircle.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
/*!
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
import { Box, type BoxProps } from "@chakra-ui/react";

import type { TaskInstanceState } from "openapi/requests/types.gen";
import { stateColor } from "src/utils/stateColor";

export const StateCircle = ({
state,
...rest
}: {
readonly state: TaskInstanceState;
} & BoxProps) => (
<Box
{...rest}
bg={stateColor[state]}
borderRadius="50%"
h={2}
maxW={2}
w={2}
/>
);
12 changes: 9 additions & 3 deletions airflow/ui/src/components/TriggerDag/TriggerDAGIconButton.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
* specific language governing permissions and limitations
* under the License.
*/
import { Box, Button } from "@chakra-ui/react";
import { Box, IconButton } from "@chakra-ui/react";
import { useDisclosure } from "@chakra-ui/react";
import { FiPlay } from "react-icons/fi";

Expand All @@ -33,9 +33,15 @@ const TriggerDAGIconButton: React.FC<Props> = ({ dag }) => {

return (
<Box>
<Button onClick={onOpen} variant="ghost">
<IconButton
aria-label={`Trigger ${dag.dag_display_name}`}
colorPalette="blue"
onClick={onOpen}
size="xs"
variant="ghost"
>
<FiPlay />
</Button>
</IconButton>

<TriggerDAGModal
dagDisplayName={dag.dag_display_name}
Expand Down
49 changes: 24 additions & 25 deletions airflow/ui/src/components/ui/Select/Trigger.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,32 +21,31 @@ import { forwardRef } from "react";

import { CloseButton } from "../CloseButton";

type TriggerProps = {
type Props = {
clearable?: boolean;
isActive?: boolean;
} & ChakraSelect.ControlProps;

export const Trigger = forwardRef<HTMLButtonElement, TriggerProps>(
(props, ref) => {
const { children, clearable, ...rest } = props;
export const Trigger = forwardRef<HTMLButtonElement, Props>((props, ref) => {
const { children, clearable, isActive, ...rest } = props;

return (
<ChakraSelect.Control {...rest}>
<ChakraSelect.Trigger ref={ref}>{children}</ChakraSelect.Trigger>
<ChakraSelect.IndicatorGroup>
{clearable ? (
<ChakraSelect.ClearTrigger asChild>
<CloseButton
focusRingWidth="2px"
focusVisibleRing="inside"
pointerEvents="auto"
size="xs"
variant="plain"
/>
</ChakraSelect.ClearTrigger>
) : undefined}
<ChakraSelect.Indicator />
</ChakraSelect.IndicatorGroup>
</ChakraSelect.Control>
);
},
);
return (
<ChakraSelect.Control {...rest}>
<ChakraSelect.Trigger ref={ref}>{children}</ChakraSelect.Trigger>
<ChakraSelect.IndicatorGroup>
{clearable ? (
<ChakraSelect.ClearTrigger asChild>
<CloseButton
focusRingWidth="2px"
focusVisibleRing="inside"
pointerEvents="auto"
size="xs"
variant="plain"
/>
</ChakraSelect.ClearTrigger>
) : undefined}
<ChakraSelect.Indicator />
</ChakraSelect.IndicatorGroup>
</ChakraSelect.Control>
);
});
67 changes: 54 additions & 13 deletions airflow/ui/src/pages/DagsList/DagsFilters.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,24 +18,27 @@
*/
import {
Box,
Button,
createListCollection,
Field,
HStack,
type SelectValueChangeDetails,
} from "@chakra-ui/react";
import { Select as ReactSelect } from "chakra-react-select";
import type { MultiValue } from "chakra-react-select";
import { Select as ReactSelect, type MultiValue } from "chakra-react-select";
import { useCallback } from "react";
import { LuX } from "react-icons/lu";
import { useSearchParams } from "react-router-dom";

import { useDagServiceGetDagTags } from "openapi/queries";
import { useTableURLState } from "src/components/DataTable/useTableUrlState";
import { QuickFilterButton } from "src/components/QuickFilterButton";
import { StateCircle } from "src/components/StateCircle";
import { Select } from "src/components/ui";
import {
SearchParamsKeys,
type SearchParamsKeysType,
} from "src/constants/searchParams";
import { pluralize } from "src/utils";

const {
LAST_DAG_RUN_STATE: LAST_DAG_RUN_STATE_PARAM,
Expand Down Expand Up @@ -119,36 +122,59 @@ export const DagsFilters = () => {
[searchParams, setSearchParams],
);

const onClearFilters = () => {
searchParams.delete(PAUSED_PARAM);
searchParams.delete(LAST_DAG_RUN_STATE_PARAM);
searchParams.delete(TAGS_PARAM);

setSearchParams(searchParams);
};

let filterCount = 0;

if (state !== null) {
filterCount += 1;
}
if (showPaused !== null) {
filterCount += 1;
}
if (selectedTags.length > 0) {
filterCount += 1;
}

return (
<HStack justifyContent="space-between">
<HStack gap={4}>
<HStack>
<QuickFilterButton
active={isAll}
isActive={isAll}
onClick={handleStateChange}
value="all"
>
All
</QuickFilterButton>
<QuickFilterButton
active={isFailed}
isActive={isFailed}
onClick={handleStateChange}
value="failed"
>
<StateCircle state="failed" />
Failed
</QuickFilterButton>
<QuickFilterButton
active={isRunning}
isActive={isRunning}
onClick={handleStateChange}
value="running"
>
<StateCircle state="running" />
Running
</QuickFilterButton>
<QuickFilterButton
active={isSuccess}
isActive={isSuccess}
onClick={handleStateChange}
value="success"
>
<StateCircle state="success" />
Success
</QuickFilterButton>
</HStack>
Expand All @@ -157,7 +183,7 @@ export const DagsFilters = () => {
onValueChange={handlePausedChange}
value={showPaused === null ? ["All"] : [showPaused]}
>
<Select.Trigger>
<Select.Trigger colorPalette="blue" isActive={Boolean(showPaused)}>
<Select.ValueText width={20} />
</Select.Trigger>
<Select.Content>
Expand All @@ -168,16 +194,22 @@ export const DagsFilters = () => {
))}
</Select.Content>
</Select.Root>
</HStack>
<Box>
<Field.Root>
<ReactSelect
aria-label="Filter Dags by tag"
chakraStyles={{
clearIndicator: (provided) => ({
...provided,
color: "gray.fg",
}),
container: (provided) => ({
...provided,
minWidth: 64,
}),
control: (provided) => ({
...provided,
colorPalette: "blue",
}),
menu: (provided) => ({
...provided,
zIndex: 2,
Expand All @@ -187,17 +219,26 @@ export const DagsFilters = () => {
isMulti
noOptionsMessage={() => "No tags found"}
onChange={handleSelectTagsChange}
options={data?.tags.map((tag) => ({
label: tag,
value: tag,
}))}
options={
data?.tags.map((tag) => ({
label: tag,
value: tag,
})) ?? []
}
placeholder="Filter by tag"
value={selectedTags.map((tag) => ({
label: tag,
value: tag,
}))}
/>
</Field.Root>
</HStack>
<Box>
{filterCount > 0 && (
<Button onClick={onClearFilters} size="sm" variant="outline">
<LuX /> Reset {pluralize("filter", filterCount)}
</Button>
)}
</Box>
</HStack>
);
Expand Down
Loading

0 comments on commit 61076f0

Please sign in to comment.