This is an automated email from the ASF dual-hosted git repository.
bbovenzi pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/airflow.git
The following commit(s) were added to refs/heads/main by this push:
new dfdb925e1bc UI: Stop using React.FC and the React type namespace
(#71563)
dfdb925e1bc is described below
commit dfdb925e1bcabc5727661a282a5e53be7944c81d
Author: Ryan Hamilton <[email protected]>
AuthorDate: Tue Aug 18 10:02:55 2026 -0400
UI: Stop using React.FC and the React type namespace (#71563)
React.FC adds nothing over annotating the props parameter directly, and the
UI
packages were split between the two styles. All of them compile with the
automatic JSX runtime, so no file needs a React import at all -- the
namespace
survived only as habit.
ESLint now rejects both patterns so they cannot creep back in. Two rules are
needed rather than one: @types/react declares `export as namespace React`,
so
`React.FC` type-checks in files that never import React, and banning the
import
alone would miss those.
src/main.tsx keeps its namespace import -- it republishes the host's React
to
UMD-bundled plugins, which resolve it through a `React` global.
---
.../auth/managers/simple/ui/rules/core.js | 37 ++++++++++++++++++++++
.../auth/managers/simple/ui/rules/typescript.js | 10 ++++++
.../auth/managers/simple/ui/src/alert/Alert.tsx | 10 +++---
.../managers/simple/ui/src/alert/ErrorAlert.tsx | 3 +-
.../managers/simple/ui/src/login/LoginForm.tsx | 4 +--
airflow-core/src/airflow/ui/rules/core.js | 37 ++++++++++++++++++++++
airflow-core/src/airflow/ui/rules/typescript.js | 10 ++++++
.../src/airflow/ui/src/components/AnsiRenderer.tsx | 9 +++---
.../src/airflow/ui/src/components/ConfigForm.tsx | 7 ++--
.../airflow/ui/src/components/DAGWarningsModal.tsx | 10 +++---
.../src/airflow/ui/src/components/DeleteDialog.tsx | 5 ++-
.../ui/src/components/FilterBar/FilterBar.tsx | 5 ++-
.../ui/src/components/FilterBar/FilterPill.tsx | 10 +++---
.../ui/src/components/FilterBar/defaultIcons.tsx | 5 +--
.../components/FilterBar/filters/DateFilter.tsx | 3 +-
.../src/components/FilterBar/filters/DateInput.tsx | 4 +--
.../FilterBar/filters/DateRangeFilter.test.tsx | 3 +-
.../FilterBar/filters/DateRangeInputs.tsx | 7 ++--
.../FilterBar/filters/TextSearchFilter.tsx | 4 +--
.../airflow/ui/src/components/FilterBar/types.ts | 6 ++--
.../src/components/FlexibleForm/FlexibleForm.tsx | 4 +--
.../airflow/ui/src/components/LimitedItemsList.tsx | 6 ++--
.../ui/src/components/SearchDags/SearchDags.tsx | 8 ++---
.../SearchDags/SearchDagsDropdownIndicator.tsx | 2 +-
.../src/airflow/ui/src/components/StateBadge.tsx | 4 +--
.../src/airflow/ui/src/components/StatsCard.tsx | 3 +-
.../src/components/TriggerDag/TriggerDAGModal.tsx | 6 ++--
.../ui/src/components/renderStructuredLog.tsx | 12 +++----
.../ui/src/components/ui/ActionBar/BarContent.tsx | 4 +--
.../src/airflow/ui/src/components/ui/Alert.tsx | 10 +++---
.../ui/src/components/ui/Breadcrumb/Root.tsx | 12 +++----
.../src/airflow/ui/src/components/ui/Checkbox.tsx | 10 +++---
.../src/airflow/ui/src/components/ui/Clipboard.tsx | 16 +++++-----
.../ui/src/components/ui/Dialog/Content.tsx | 4 +--
.../ui/src/components/ui/InputWithAddon.tsx | 6 ++--
.../airflow/ui/src/components/ui/LazyClipboard.tsx | 6 ++--
.../ui/src/components/ui/Menu/MenuContent.tsx | 4 +--
.../airflow/ui/src/components/ui/NumberInput.tsx | 4 +--
.../ui/src/components/ui/Pagination/Items.tsx | 3 +-
.../airflow/ui/src/components/ui/Popover/Arrow.tsx | 4 +--
.../ui/src/components/ui/Popover/CloseTrigger.tsx | 14 ++++----
.../ui/src/components/ui/Popover/Content.tsx | 6 ++--
.../src/airflow/ui/src/components/ui/RadioCard.tsx | 6 ++--
.../ui/src/components/ui/Select/Content.tsx | 4 +--
.../ui/src/components/ui/Select/ItemGroup.tsx | 4 +--
.../ui/src/components/ui/Select/ValueText.tsx | 4 +--
.../src/airflow/ui/src/components/ui/Switch.tsx | 10 +++---
.../src/airflow/ui/src/components/ui/Tag.tsx | 6 ++--
.../src/airflow/ui/src/components/ui/Tooltip.tsx | 8 ++---
.../src/airflow/ui/src/hooks/useDateRangeFilter.ts | 4 +--
.../airflow/ui/src/hooks/useRequiredActionTabs.ts | 4 +--
.../ui/src/layouts/Details/PanelButtons.tsx | 6 ++--
.../airflow/ui/src/layouts/Nav/LanguageModal.tsx | 3 +-
.../ui/src/layouts/Nav/LanguageSelector.tsx | 4 +--
.../src/airflow/ui/src/layouts/Nav/LogoutModal.tsx | 3 +-
.../airflow/ui/src/layouts/Nav/TimezoneModal.tsx | 3 +-
.../ui/src/layouts/Nav/TimezoneSelector.tsx | 4 +--
.../ui/src/layouts/Nav/TokenGenerationModal.tsx | 4 +--
airflow-core/src/airflow/ui/src/main.tsx | 3 ++
.../ui/src/pages/Asset/AssetPanelButtons.tsx | 3 +-
.../pages/Dashboard/Stats/DagImportErrorsModal.tsx | 6 ++--
.../Dashboard/Stats/PluginImportErrorsModal.tsx | 12 +++----
.../ui/src/queries/useGridTISummaries.test.tsx | 4 +--
.../airflow/ui/src/queries/useTogglePause.test.tsx | 4 +--
.../react_plugin_template/eslint.config.js | 34 ++++++++++++++++++++
.../react_plugin_template/src/main.tsx | 3 +-
.../providers/edge3/plugins/www/eslint.config.js | 34 ++++++++++++++++++++
.../plugins/www/src/components/StateBadge.tsx | 4 +--
.../www/src/components/WorkerStateBadge.tsx | 4 +--
.../www/src/components/WorkerSysinfoBadge.tsx | 4 +--
.../edge3/plugins/www/src/components/ui/Alert.tsx | 10 +++---
.../plugins/www/src/components/ui/InputGroup.tsx | 8 ++---
.../www/src/components/ui/Select/Content.tsx | 4 +--
.../www/src/components/ui/Select/ValueText.tsx | 4 +--
.../providers/edge3/plugins/www/src/main.tsx | 3 +-
75 files changed, 357 insertions(+), 201 deletions(-)
diff --git
a/airflow-core/src/airflow/api_fastapi/auth/managers/simple/ui/rules/core.js
b/airflow-core/src/airflow/api_fastapi/auth/managers/simple/ui/rules/core.js
index 342b4d7d610..461195dfd07 100644
--- a/airflow-core/src/airflow/api_fastapi/auth/managers/simple/ui/rules/core.js
+++ b/airflow-core/src/airflow/api_fastapi/auth/managers/simple/ui/rules/core.js
@@ -867,6 +867,43 @@ export const coreRules = /** @type {const} @satisfies
{FlatConfig.Config} */ ({
*/
"no-restricted-globals": [ERROR, { message: "Use `globalThis` instead.",
name: "window" }],
+ /**
+ * Disallow the `React` type namespace. `@types/react` declares `export as
+ * namespace React`, so `React.ReactNode` type-checks even in files that
+ * never import React — only a syntax rule can catch those.
+ *
+ * @example
+ * ```typescript
+ * // ❌ Incorrect
+ * const Foo: React.FC<FooProps> = ({ bar }) => <div>{bar}</div>;
+ * const node: React.ReactNode = null;
+ *
+ * // ✅ Correct
+ * const Foo = ({ bar }: FooProps) => <div>{bar}</div>;
+ * const node: ReactNode = null;
+ * ```
+ * @see
[no-restricted-syntax](https://eslint.org/docs/latest/rules/no-restricted-syntax)
+ */
+ "no-restricted-syntax": [
+ ERROR,
+ {
+ message:
+ "Do not type components with `React.FC`. Annotate the props
parameter instead: `const Foo = ({ bar }: FooProps) => ...`.",
+ selector:
"TSQualifiedName[left.name='React'][right.name=/^(FC|FunctionComponent|VFC)$/]",
+ },
+ {
+ message:
+ "Do not type components with `FC`. Annotate the props parameter
instead: `const Foo = ({ bar }: FooProps) => ...`.",
+ selector:
+ "VariableDeclarator[init.type='ArrowFunctionExpression'] >
Identifier > TSTypeAnnotation > TSTypeReference >
Identifier[name=/^(FC|FunctionComponent|VFC)$/]",
+ },
+ {
+ message:
+ 'Import React types by name instead of qualifying them with the
`React` namespace: `import { type ReactElement } from "react"`, then use
`ReactElement`.',
+ selector:
"TSQualifiedName[left.name='React']:not([right.name=/^(FC|FunctionComponent|VFC)$/])",
+ },
+ ],
+
/**
* Disallow assignment operators in `return` statements.
*
diff --git
a/airflow-core/src/airflow/api_fastapi/auth/managers/simple/ui/rules/typescript.js
b/airflow-core/src/airflow/api_fastapi/auth/managers/simple/ui/rules/typescript.js
index 16109237d37..bbff0012ded 100644
---
a/airflow-core/src/airflow/api_fastapi/auth/managers/simple/ui/rules/typescript.js
+++
b/airflow-core/src/airflow/api_fastapi/auth/managers/simple/ui/rules/typescript.js
@@ -971,6 +971,8 @@ export const typescriptRules = /** @type {const} @satisfies
{FlatConfig.Config}
* - Lodash.
* - Moment.
* - Underscore.
+ * - The `React` default and namespace import — the automatic JSX runtime
+ * makes it unnecessary.
*
* @see
[@typescript-eslint/no-restricted-imports](https://typescript-eslint.io/rules/no-restricted-imports/)
* @see
[no-restricted-imports](https://eslint.org/docs/latest/rules/no-restricted-imports)
@@ -978,6 +980,14 @@ export const typescriptRules = /** @type {const}
@satisfies {FlatConfig.Config}
[`${typescriptNamespace}/no-restricted-imports`]: [
ERROR,
{
+ paths: [
+ {
+ importNames: ["default"],
+ message:
+ 'The automatic JSX runtime makes the React namespace import
unnecessary. Import what you need by name, e.g. `import { forwardRef, type
ReactNode } from "react"`.',
+ name: "react",
+ },
+ ],
patterns: [
{
group: ["jquery", "lodash", "lodash-es", "lodash.*", "lodash/*",
"underscore"],
diff --git
a/airflow-core/src/airflow/api_fastapi/auth/managers/simple/ui/src/alert/Alert.tsx
b/airflow-core/src/airflow/api_fastapi/auth/managers/simple/ui/src/alert/Alert.tsx
index 7cad431162e..e8f73d75f88 100644
---
a/airflow-core/src/airflow/api_fastapi/auth/managers/simple/ui/src/alert/Alert.tsx
+++
b/airflow-core/src/airflow/api_fastapi/auth/managers/simple/ui/src/alert/Alert.tsx
@@ -17,15 +17,15 @@
* under the License.
*/
import { Alert as ChakraAlert, IconButton } from "@chakra-ui/react";
-import { forwardRef } from "react";
+import { forwardRef, type ReactNode, type ReactElement } from "react";
export type AlertProps = {
readonly closable?: boolean;
- readonly endElement?: React.ReactNode;
- readonly icon?: React.ReactElement;
+ readonly endElement?: ReactNode;
+ readonly icon?: ReactElement;
readonly onClose?: () => void;
- readonly startElement?: React.ReactNode;
- readonly title?: React.ReactNode;
+ readonly startElement?: ReactNode;
+ readonly title?: ReactNode;
} & Omit<ChakraAlert.RootProps, "title">;
export const Alert = forwardRef<HTMLDivElement, AlertProps>((props, ref) => {
diff --git
a/airflow-core/src/airflow/api_fastapi/auth/managers/simple/ui/src/alert/ErrorAlert.tsx
b/airflow-core/src/airflow/api_fastapi/auth/managers/simple/ui/src/alert/ErrorAlert.tsx
index 12c29dab3f9..2ae81639711 100644
---
a/airflow-core/src/airflow/api_fastapi/auth/managers/simple/ui/src/alert/ErrorAlert.tsx
+++
b/airflow-core/src/airflow/api_fastapi/auth/managers/simple/ui/src/alert/ErrorAlert.tsx
@@ -18,6 +18,7 @@
*/
import { HStack } from "@chakra-ui/react";
import type { AxiosError } from "axios";
+import type { ReactNode } from "react";
import type { HttpExceptionResponse, HttpValidationError } from
"openapi/requests/types.gen";
@@ -37,7 +38,7 @@ export const ErrorAlert = ({ error: err }: Props) => {
const statusCode = response?.status;
const statusText = response?.statusText ?? message;
const detail = response?.data.detail;
- let detailMessage: React.ReactNode;
+ let detailMessage: ReactNode;
if (typeof detail === "string") {
detailMessage = detail;
diff --git
a/airflow-core/src/airflow/api_fastapi/auth/managers/simple/ui/src/login/LoginForm.tsx
b/airflow-core/src/airflow/api_fastapi/auth/managers/simple/ui/src/login/LoginForm.tsx
index 0b466e02274..06f3cacfebd 100644
---
a/airflow-core/src/airflow/api_fastapi/auth/managers/simple/ui/src/login/LoginForm.tsx
+++
b/airflow-core/src/airflow/api_fastapi/auth/managers/simple/ui/src/login/LoginForm.tsx
@@ -17,7 +17,7 @@
* under the License.
*/
import { Button, Field, Input, Stack } from "@chakra-ui/react";
-import React from "react";
+import type { SyntheticEvent } from "react";
import { Controller, useForm } from "react-hook-form";
import type { LoginBody } from "./Login";
@@ -41,7 +41,7 @@ export const LoginForm = ({ isPending, onLogin }:
LoginFormProps) => {
return (
<form
- onSubmit={(event: React.SyntheticEvent) => {
+ onSubmit={(event: SyntheticEvent) => {
event.preventDefault();
void handleSubmit(onLogin)();
}}
diff --git a/airflow-core/src/airflow/ui/rules/core.js
b/airflow-core/src/airflow/ui/rules/core.js
index 8ed46f24ffd..c5ba3e7a501 100644
--- a/airflow-core/src/airflow/ui/rules/core.js
+++ b/airflow-core/src/airflow/ui/rules/core.js
@@ -885,6 +885,43 @@ export const coreRules = /** @type {const} @satisfies
{FlatConfig.Config} */ ({
*/
"no-restricted-globals": [ERROR, { message: "Use `globalThis` instead.",
name: "window" }],
+ /**
+ * Disallow the `React` type namespace. `@types/react` declares `export as
+ * namespace React`, so `React.ReactNode` type-checks even in files that
+ * never import React — only a syntax rule can catch those.
+ *
+ * @example
+ * ```typescript
+ * // ❌ Incorrect
+ * const Foo: React.FC<FooProps> = ({ bar }) => <div>{bar}</div>;
+ * const node: React.ReactNode = null;
+ *
+ * // ✅ Correct
+ * const Foo = ({ bar }: FooProps) => <div>{bar}</div>;
+ * const node: ReactNode = null;
+ * ```
+ * @see
[no-restricted-syntax](https://eslint.org/docs/latest/rules/no-restricted-syntax)
+ */
+ "no-restricted-syntax": [
+ ERROR,
+ {
+ message:
+ "Do not type components with `React.FC`. Annotate the props
parameter instead: `const Foo = ({ bar }: FooProps) => ...`.",
+ selector:
"TSQualifiedName[left.name='React'][right.name=/^(FC|FunctionComponent|VFC)$/]",
+ },
+ {
+ message:
+ "Do not type components with `FC`. Annotate the props parameter
instead: `const Foo = ({ bar }: FooProps) => ...`.",
+ selector:
+ "VariableDeclarator[init.type='ArrowFunctionExpression'] >
Identifier > TSTypeAnnotation > TSTypeReference >
Identifier[name=/^(FC|FunctionComponent|VFC)$/]",
+ },
+ {
+ message:
+ 'Import React types by name instead of qualifying them with the
`React` namespace: `import { type ReactElement } from "react"`, then use
`ReactElement`.',
+ selector:
"TSQualifiedName[left.name='React']:not([right.name=/^(FC|FunctionComponent|VFC)$/])",
+ },
+ ],
+
/**
* Disallow assignment operators in `return` statements.
*
diff --git a/airflow-core/src/airflow/ui/rules/typescript.js
b/airflow-core/src/airflow/ui/rules/typescript.js
index cceb7b676c9..a09d1e7b9b7 100644
--- a/airflow-core/src/airflow/ui/rules/typescript.js
+++ b/airflow-core/src/airflow/ui/rules/typescript.js
@@ -971,6 +971,8 @@ export const typescriptRules = /** @type {const} @satisfies
{FlatConfig.Config}
* - Lodash.
* - Moment.
* - Underscore.
+ * - The `React` default and namespace import — the automatic JSX runtime
+ * makes it unnecessary.
*
* @see
[@typescript-eslint/no-restricted-imports](https://typescript-eslint.io/rules/no-restricted-imports/)
* @see
[no-restricted-imports](https://eslint.org/docs/latest/rules/no-restricted-imports)
@@ -978,6 +980,14 @@ export const typescriptRules = /** @type {const}
@satisfies {FlatConfig.Config}
[`${typescriptNamespace}/no-restricted-imports`]: [
ERROR,
{
+ paths: [
+ {
+ importNames: ["default"],
+ message:
+ 'The automatic JSX runtime makes the React namespace import
unnecessary. Import what you need by name, e.g. `import { forwardRef, type
ReactNode } from "react"`.',
+ name: "react",
+ },
+ ],
patterns: [
{
group: ["jquery", "lodash", "lodash-es", "lodash.*", "lodash/*",
"underscore"],
diff --git a/airflow-core/src/airflow/ui/src/components/AnsiRenderer.tsx
b/airflow-core/src/airflow/ui/src/components/AnsiRenderer.tsx
index 03809a0a7f6..c0abf369c6d 100644
--- a/airflow-core/src/airflow/ui/src/components/AnsiRenderer.tsx
+++ b/airflow-core/src/airflow/ui/src/components/AnsiRenderer.tsx
@@ -18,8 +18,7 @@
*/
import { chakra } from "@chakra-ui/react";
import Anser, { type AnserJsonEntry } from "anser";
-import type { JSX } from "react";
-import * as React from "react";
+import type { JSX, ReactNode } from "react";
const fixBackspace = (inputText: string): string => {
let tmp = inputText;
@@ -166,7 +165,7 @@ const convertBundleIntoReact = (options: {
);
}
- const content: Array<React.ReactNode> = [];
+ const content: Array<ReactNode> = [];
const linkRegex =
/(?<whitespace>\s|^)(?<url>https?:\/\/(?:www\.|(?!www))[^\s.]+\.\S{2,}|www\.\S+\.\S{2,})/gu;
@@ -220,12 +219,12 @@ type AnsiRendererProps = {
readonly useClasses?: boolean;
};
-export const AnsiRenderer: React.FC<AnsiRendererProps> = ({
+export const AnsiRenderer = ({
children = "",
className,
linkify = false,
useClasses = false,
-}) => (
+}: AnsiRendererProps) => (
<code className={className}>
{ansiToJSON(children, useClasses).map((bundle, index) =>
convertBundleIntoReact({ bundle, key: index, linkify, useClasses }),
diff --git a/airflow-core/src/airflow/ui/src/components/ConfigForm.tsx
b/airflow-core/src/airflow/ui/src/components/ConfigForm.tsx
index 267560c7c4c..2ed1ee7ce0b 100644
--- a/airflow-core/src/airflow/ui/src/components/ConfigForm.tsx
+++ b/airflow-core/src/airflow/ui/src/components/ConfigForm.tsx
@@ -17,6 +17,7 @@
* under the License.
*/
import { Box, Field } from "@chakra-ui/react";
+import type { ReactNode, Dispatch, SetStateAction } from "react";
import { type Control, type FieldValues, type Path, Controller } from
"react-hook-form";
import { useTranslation } from "react-i18next";
@@ -28,15 +29,15 @@ import { JsonEditor } from "./JsonEditor";
import { Accordion } from "./ui";
type ConfigFormProps<T extends FieldValues = FieldValues> = {
- readonly children?: React.ReactNode;
+ readonly children?: ReactNode;
readonly control: Control<T>;
readonly errors: {
conf?: string;
date?: unknown;
};
readonly initialParamsDict: { paramsDict: ParamsSpec };
- readonly setErrors: React.Dispatch<
- React.SetStateAction<{
+ readonly setErrors: Dispatch<
+ SetStateAction<{
conf?: string;
date?: unknown;
}>
diff --git a/airflow-core/src/airflow/ui/src/components/DAGWarningsModal.tsx
b/airflow-core/src/airflow/ui/src/components/DAGWarningsModal.tsx
index 6c85b8d56b6..e5f383fcded 100644
--- a/airflow-core/src/airflow/ui/src/components/DAGWarningsModal.tsx
+++ b/airflow-core/src/airflow/ui/src/components/DAGWarningsModal.tsx
@@ -27,13 +27,13 @@ import { ErrorAlert } from "./ErrorAlert";
import { WarningAlert } from "./WarningAlert";
type ImportDAGErrorModalProps = {
- error?: unknown;
- onClose: () => void;
- open: boolean;
- warnings?: Array<DAGWarningResponse>;
+ readonly error?: unknown;
+ readonly onClose: () => void;
+ readonly open: boolean;
+ readonly warnings?: Array<DAGWarningResponse>;
};
-export const DAGWarningsModal: React.FC<ImportDAGErrorModalProps> = ({ error,
onClose, open, warnings }) => {
+export const DAGWarningsModal = ({ error, onClose, open, warnings }:
ImportDAGErrorModalProps) => {
const { t: translate } = useTranslation("components");
const heading = Boolean(error)
? warnings?.length !== undefined && warnings.length > 0
diff --git a/airflow-core/src/airflow/ui/src/components/DeleteDialog.tsx
b/airflow-core/src/airflow/ui/src/components/DeleteDialog.tsx
index 5f57d171ba1..0652296f5e7 100644
--- a/airflow-core/src/airflow/ui/src/components/DeleteDialog.tsx
+++ b/airflow-core/src/airflow/ui/src/components/DeleteDialog.tsx
@@ -17,7 +17,6 @@
* under the License.
*/
import { Button, Heading, HStack, Text } from "@chakra-ui/react";
-import React from "react";
import { useTranslation } from "react-i18next";
import { FiTrash2 } from "react-icons/fi";
@@ -34,7 +33,7 @@ type DeleteDialogProps = {
readonly warningText: string;
};
-const DeleteDialog: React.FC<DeleteDialogProps> = ({
+const DeleteDialog = ({
deleteButtonText,
isDeleting,
onClose,
@@ -43,7 +42,7 @@ const DeleteDialog: React.FC<DeleteDialogProps> = ({
resourceName,
title,
warningText,
-}) => {
+}: DeleteDialogProps) => {
const { t: translate } = useTranslation("common");
return (
diff --git a/airflow-core/src/airflow/ui/src/components/FilterBar/FilterBar.tsx
b/airflow-core/src/airflow/ui/src/components/FilterBar/FilterBar.tsx
index 00d99ab8b01..648ab136cbb 100644
--- a/airflow-core/src/airflow/ui/src/components/FilterBar/FilterBar.tsx
+++ b/airflow-core/src/airflow/ui/src/components/FilterBar/FilterBar.tsx
@@ -17,7 +17,7 @@
* under the License.
*/
import { Button, Flex, HStack } from "@chakra-ui/react";
-import { useEffect, useState } from "react";
+import { useEffect, useState, type ReactNode } from "react";
import { useTranslation } from "react-i18next";
import { MdAdd, MdClear } from "react-icons/md";
import { useDebouncedCallback } from "use-debounce";
@@ -36,8 +36,7 @@ import { getDefaultFilterValue, isValidFilterValue } from
"./utils";
const defaultInitialValues: Record<string, FilterValue> = {};
-const getFilterIcon = (config: FilterConfig): React.ReactNode =>
- config.icon ?? getDefaultFilterIcon(config.type);
+const getFilterIcon = (config: FilterConfig): ReactNode => config.icon ??
getDefaultFilterIcon(config.type);
export const FilterBar = ({
configs,
diff --git
a/airflow-core/src/airflow/ui/src/components/FilterBar/FilterPill.tsx
b/airflow-core/src/airflow/ui/src/components/FilterBar/FilterPill.tsx
index 24914cf4240..1f0ad91dc3d 100644
--- a/airflow-core/src/airflow/ui/src/components/FilterBar/FilterPill.tsx
+++ b/airflow-core/src/airflow/ui/src/components/FilterBar/FilterPill.tsx
@@ -17,7 +17,7 @@
* under the License.
*/
import { Box, HStack } from "@chakra-ui/react";
-import type { RefObject } from "react";
+import type { RefObject, KeyboardEvent, ReactNode } from "react";
import { useEffect, useRef, useState } from "react";
import { MdClose } from "react-icons/md";
@@ -28,16 +28,16 @@ import { isEmptyFilterValue } from "./utils";
export type FilterPillInputProps = {
onBlur: () => void;
onFocus: () => void;
- onKeyDown: (event: React.KeyboardEvent) => void;
+ onKeyDown: (event: KeyboardEvent) => void;
ref: RefObject<HTMLInputElement | null>;
};
type FilterPillProps = {
- readonly displayValue: React.ReactNode | string;
+ readonly displayValue: ReactNode | string;
readonly filter: FilterState;
readonly hasValue: boolean;
readonly onRemove: () => void;
- readonly renderInput: (props: FilterPillInputProps) => React.ReactNode;
+ readonly renderInput: (props: FilterPillInputProps) => ReactNode;
};
export const FilterPill = ({ displayValue, filter, hasValue, onRemove,
renderInput }: FilterPillProps) => {
@@ -48,7 +48,7 @@ export const FilterPill = ({ displayValue, filter, hasValue,
onRemove, renderInp
const handlePillClick = () => setIsEditing(true);
- const handleKeyDown = (event: React.KeyboardEvent) => {
+ const handleKeyDown = (event: KeyboardEvent) => {
if (event.key === "Enter" || event.key === "Escape") {
setIsEditing(false);
}
diff --git
a/airflow-core/src/airflow/ui/src/components/FilterBar/defaultIcons.tsx
b/airflow-core/src/airflow/ui/src/components/FilterBar/defaultIcons.tsx
index 62edfccb45c..aba7fb2470f 100644
--- a/airflow-core/src/airflow/ui/src/components/FilterBar/defaultIcons.tsx
+++ b/airflow-core/src/airflow/ui/src/components/FilterBar/defaultIcons.tsx
@@ -16,11 +16,12 @@
* specific language governing permissions and limitations
* under the License.
*/
+import type { ReactNode } from "react";
import { MdCalendarToday, MdNumbers, MdTextFields, MdArrowDropDown,
MdDateRange } from "react-icons/md";
import type { FilterConfig } from "./types";
-export const defaultFilterIcons: Record<FilterConfig["type"], React.ReactNode>
= {
+export const defaultFilterIcons: Record<FilterConfig["type"], ReactNode> = {
date: <MdCalendarToday />,
daterange: <MdDateRange />,
number: <MdNumbers />,
@@ -28,4 +29,4 @@ export const defaultFilterIcons: Record<FilterConfig["type"],
React.ReactNode> =
text: <MdTextFields />,
} as const;
-export const getDefaultFilterIcon = (type: FilterConfig["type"]):
React.ReactNode => defaultFilterIcons[type];
+export const getDefaultFilterIcon = (type: FilterConfig["type"]): ReactNode =>
defaultFilterIcons[type];
diff --git
a/airflow-core/src/airflow/ui/src/components/FilterBar/filters/DateFilter.tsx
b/airflow-core/src/airflow/ui/src/components/FilterBar/filters/DateFilter.tsx
index a8d7c2c7f91..fbf00ed4b68 100644
---
a/airflow-core/src/airflow/ui/src/components/FilterBar/filters/DateFilter.tsx
+++
b/airflow-core/src/airflow/ui/src/components/FilterBar/filters/DateFilter.tsx
@@ -17,6 +17,7 @@
* under the License.
*/
import dayjs from "dayjs";
+import type { ChangeEvent } from "react";
import { DateTimeInput } from "src/components/DateTimeInput";
@@ -29,7 +30,7 @@ export const DateFilter = ({ filter, onChange, onRemove }:
FilterPluginProps) =>
const displayValue =
hasValue && typeof filter.value === "string" ?
dayjs(filter.value).format("MMM DD, YYYY, hh:mm A") : "";
- const handleDateChange = (event: React.ChangeEvent<HTMLInputElement>) => {
+ const handleDateChange = (event: ChangeEvent<HTMLInputElement>) => {
const { value } = event.target;
onChange(value || undefined);
diff --git
a/airflow-core/src/airflow/ui/src/components/FilterBar/filters/DateInput.tsx
b/airflow-core/src/airflow/ui/src/components/FilterBar/filters/DateInput.tsx
index 0856c80dd9d..21e38a1a58b 100644
--- a/airflow-core/src/airflow/ui/src/components/FilterBar/filters/DateInput.tsx
+++ b/airflow-core/src/airflow/ui/src/components/FilterBar/filters/DateInput.tsx
@@ -17,7 +17,7 @@
* under the License.
*/
import { Box, Input, Text } from "@chakra-ui/react";
-import React from "react";
+import type { ChangeEvent } from "react";
import { MdClose } from "react-icons/md";
import { IconButton } from "src/components/ui";
@@ -30,7 +30,7 @@ type DateInputProps = {
readonly handleInputChange: (
field: "end" | "start",
inputType: "date" | "time",
- ) => (event: React.ChangeEvent<HTMLInputElement>) => void;
+ ) => (event: ChangeEvent<HTMLInputElement>) => void;
readonly inputType: "date" | "time";
readonly inputValue: string;
readonly label: string;
diff --git
a/airflow-core/src/airflow/ui/src/components/FilterBar/filters/DateRangeFilter.test.tsx
b/airflow-core/src/airflow/ui/src/components/FilterBar/filters/DateRangeFilter.test.tsx
index 5edf7bfbe66..4e47260f385 100644
---
a/airflow-core/src/airflow/ui/src/components/FilterBar/filters/DateRangeFilter.test.tsx
+++
b/airflow-core/src/airflow/ui/src/components/FilterBar/filters/DateRangeFilter.test.tsx
@@ -21,6 +21,7 @@ import { render, screen, fireEvent, waitFor, cleanup } from
"@testing-library/re
import dayjs from "dayjs";
import timezone from "dayjs/plugin/timezone";
import utc from "dayjs/plugin/utc";
+import type { ReactNode } from "react";
import { describe, it, expect, vi, beforeEach, afterEach } from "vitest";
import { TimezoneContext } from "src/context/timezone";
@@ -55,7 +56,7 @@ vi.mock("react-i18next", () => ({
}),
}));
-const TestWrapper = ({ children }: { readonly children: React.ReactNode }) => {
+const TestWrapper = ({ children }: { readonly children: ReactNode }) => {
const timezoneContextValue = {
availableTimezones: ["UTC", "America/New_York"],
selectedTimezone: "UTC",
diff --git
a/airflow-core/src/airflow/ui/src/components/FilterBar/filters/DateRangeInputs.tsx
b/airflow-core/src/airflow/ui/src/components/FilterBar/filters/DateRangeInputs.tsx
index 61f1f792458..04af57c24c0 100644
---
a/airflow-core/src/airflow/ui/src/components/FilterBar/filters/DateRangeInputs.tsx
+++
b/airflow-core/src/airflow/ui/src/components/FilterBar/filters/DateRangeInputs.tsx
@@ -19,6 +19,7 @@
import { HStack, Text, VStack } from "@chakra-ui/react";
import dayjs from "dayjs";
import type { TFunction } from "i18next";
+import type { ChangeEvent, Dispatch, SetStateAction } from "react";
import { MdAccessTime } from "react-icons/md";
import { useTimezone } from "src/context/timezone";
@@ -35,9 +36,9 @@ type DateRangeInputsProps = {
readonly handleInputChange: (
field: "end" | "start",
inputType: "date" | "time",
- ) => (event: React.ChangeEvent<HTMLInputElement>) => void;
+ ) => (event: ChangeEvent<HTMLInputElement>) => void;
readonly onChange: (value: DateRangeValue) => void;
- readonly setEditingState:
React.Dispatch<React.SetStateAction<DateRangeEditingState>>;
+ readonly setEditingState: Dispatch<SetStateAction<DateRangeEditingState>>;
readonly startDateValue: dayjs.Dayjs | undefined;
readonly translate: TFunction;
readonly value: DateRangeValue;
@@ -111,7 +112,7 @@ export const DateRangeInputs = ({
"time",
)({
target: { value: "" },
- } as React.ChangeEvent<HTMLInputElement>);
+ } as ChangeEvent<HTMLInputElement>);
};
return (
diff --git
a/airflow-core/src/airflow/ui/src/components/FilterBar/filters/TextSearchFilter.tsx
b/airflow-core/src/airflow/ui/src/components/FilterBar/filters/TextSearchFilter.tsx
index 09f1c5d3ee4..ce36f8a47cd 100644
---
a/airflow-core/src/airflow/ui/src/components/FilterBar/filters/TextSearchFilter.tsx
+++
b/airflow-core/src/airflow/ui/src/components/FilterBar/filters/TextSearchFilter.tsx
@@ -17,7 +17,7 @@
* under the License.
*/
import { HStack } from "@chakra-ui/react";
-import { useRef } from "react";
+import { useRef, type ChangeEvent } from "react";
import { LuRegex } from "react-icons/lu";
import { AdvancedSearchToggle } from "src/components/AdvancedSearchToggle";
@@ -37,7 +37,7 @@ export const TextSearchFilter = ({ filter, onChange, onRemove
}: FilterPluginPro
const hasValue = isValidFilterValue(filter.config.type, filter.value);
- const handleInputChange = (event: React.ChangeEvent<HTMLInputElement>) => {
+ const handleInputChange = (event: ChangeEvent<HTMLInputElement>) => {
const newValue = event.target.value;
onChange(newValue || undefined);
diff --git a/airflow-core/src/airflow/ui/src/components/FilterBar/types.ts
b/airflow-core/src/airflow/ui/src/components/FilterBar/types.ts
index 2524fe64f43..bb096da58b1 100644
--- a/airflow-core/src/airflow/ui/src/components/FilterBar/types.ts
+++ b/airflow-core/src/airflow/ui/src/components/FilterBar/types.ts
@@ -16,7 +16,7 @@
* specific language governing permissions and limitations
* under the License.
*/
-import type React from "react";
+import type { ReactNode } from "react";
export type DateRangeValue = {
endDate?: string;
@@ -29,12 +29,12 @@ export type FilterConfig = {
readonly defaultValue?: FilterValue;
readonly endKey?: string;
readonly hotkeyDisabled?: boolean;
- readonly icon?: React.ReactNode;
+ readonly icon?: ReactNode;
readonly key: string;
readonly label: string;
readonly max?: number;
readonly min?: number;
- readonly options?: Array<{ label: React.ReactNode | string; value: string }>;
+ readonly options?: Array<{ label: ReactNode | string; value: string }>;
readonly placeholder?: string;
readonly required?: boolean;
readonly startKey?: string;
diff --git
a/airflow-core/src/airflow/ui/src/components/FlexibleForm/FlexibleForm.tsx
b/airflow-core/src/airflow/ui/src/components/FlexibleForm/FlexibleForm.tsx
index 2e1e5c5568a..fc6721ce7a3 100644
--- a/airflow-core/src/airflow/ui/src/components/FlexibleForm/FlexibleForm.tsx
+++ b/airflow-core/src/airflow/ui/src/components/FlexibleForm/FlexibleForm.tsx
@@ -17,7 +17,7 @@
* under the License.
*/
import { Icon, Stack, StackSeparator, Text } from "@chakra-ui/react";
-import { useEffect, useState } from "react";
+import { useEffect, useState, type ReactNode } from "react";
import { MdError } from "react-icons/md";
import type { ParamsSpec, ParamSpec } from "src/queries/useDagParams";
@@ -33,7 +33,7 @@ const FlatSection = ({
hasError,
title,
}: {
- readonly children: React.ReactNode;
+ readonly children: ReactNode;
readonly hasError: boolean;
readonly title: string;
}) => (
diff --git a/airflow-core/src/airflow/ui/src/components/LimitedItemsList.tsx
b/airflow-core/src/airflow/ui/src/components/LimitedItemsList.tsx
index 60877a7439a..177492f9696 100644
--- a/airflow-core/src/airflow/ui/src/components/LimitedItemsList.tsx
+++ b/airflow-core/src/airflow/ui/src/components/LimitedItemsList.tsx
@@ -17,7 +17,7 @@
* under the License.
*/
import { Box, Button, Text, HStack, Stack } from "@chakra-ui/react";
-import React, { type ReactNode } from "react";
+import { type ReactNode, Fragment } from "react";
import { useTranslation } from "react-i18next";
import { Popover } from "./ui";
@@ -52,13 +52,13 @@ export const LimitedItemsList = ({
<Box fontSize="sm">
{displayItems.map((item, index) => (
// eslint-disable-next-line react/no-array-index-key
- <React.Fragment key={index}>
+ <Fragment key={index}>
<Text as="span">{item}</Text>
{index < displayItems.length - 1 ||
(shouldTruncate && remainingItems.length >= 1 && index ===
displayItems.length - 1) ? (
<Text as="span">{separator}</Text>
) : undefined}
- </React.Fragment>
+ </Fragment>
))}
{shouldTruncate ? (
remainingItems.length === 1 ? (
diff --git
a/airflow-core/src/airflow/ui/src/components/SearchDags/SearchDags.tsx
b/airflow-core/src/airflow/ui/src/components/SearchDags/SearchDags.tsx
index d85a6a5746c..96b9677bf1c 100644
--- a/airflow-core/src/airflow/ui/src/components/SearchDags/SearchDags.tsx
+++ b/airflow-core/src/airflow/ui/src/components/SearchDags/SearchDags.tsx
@@ -20,7 +20,7 @@ import { Field, Flex, Text } from "@chakra-ui/react";
import { useQueryClient } from "@tanstack/react-query";
import { AsyncSelect } from "chakra-react-select";
import type { OptionsOrGroups, GroupBase, SingleValue } from
"chakra-react-select";
-import React from "react";
+import type { Dispatch, SetStateAction } from "react";
import { useTranslation } from "react-i18next";
import { useNavigate } from "react-router-dom";
import { useDebouncedCallback } from "use-debounce";
@@ -43,11 +43,7 @@ const formatOptionLabel = (option: DagSearchOption) => (
</Flex>
);
-export const SearchDags = ({
- setIsOpen,
-}: {
- readonly setIsOpen: React.Dispatch<React.SetStateAction<boolean>>;
-}) => {
+export const SearchDags = ({ setIsOpen }: { readonly setIsOpen:
Dispatch<SetStateAction<boolean>> }) => {
const { t: translate } = useTranslation("dags");
const queryClient = useQueryClient();
const navigate = useNavigate();
diff --git
a/airflow-core/src/airflow/ui/src/components/SearchDags/SearchDagsDropdownIndicator.tsx
b/airflow-core/src/airflow/ui/src/components/SearchDags/SearchDagsDropdownIndicator.tsx
index cffc8aaf2ac..45568a2cd43 100644
---
a/airflow-core/src/airflow/ui/src/components/SearchDags/SearchDagsDropdownIndicator.tsx
+++
b/airflow-core/src/airflow/ui/src/components/SearchDags/SearchDagsDropdownIndicator.tsx
@@ -22,7 +22,7 @@ import { FiSearch } from "react-icons/fi";
import type { DagSearchOption } from "src/utils/option";
-export const DropdownIndicator:
React.FC<DropdownIndicatorProps<DagSearchOption, false>> = (props) => (
+export const DropdownIndicator = (props:
DropdownIndicatorProps<DagSearchOption, false>) => (
<chakraComponents.DropdownIndicator {...props}>
<FiSearch />
</chakraComponents.DropdownIndicator>
diff --git a/airflow-core/src/airflow/ui/src/components/StateBadge.tsx
b/airflow-core/src/airflow/ui/src/components/StateBadge.tsx
index c74193d76b0..a29c78c84fd 100644
--- a/airflow-core/src/airflow/ui/src/components/StateBadge.tsx
+++ b/airflow-core/src/airflow/ui/src/components/StateBadge.tsx
@@ -17,7 +17,7 @@
* under the License.
*/
import { Badge, type BadgeProps } from "@chakra-ui/react";
-import * as React from "react";
+import { forwardRef } from "react";
import type { TaskInstanceState } from "openapi/requests/types.gen";
@@ -27,7 +27,7 @@ export type Props = {
readonly state?: TaskInstanceState | null;
} & BadgeProps;
-export const StateBadge = React.forwardRef<HTMLDivElement, Props>(({ children,
state, ...rest }, ref) => (
+export const StateBadge = forwardRef<HTMLDivElement, Props>(({ children,
state, ...rest }, ref) => (
<Badge
borderRadius="full"
color={`${state === null ? undefined : state}.contrast`}
diff --git a/airflow-core/src/airflow/ui/src/components/StatsCard.tsx
b/airflow-core/src/airflow/ui/src/components/StatsCard.tsx
index 12e75186f54..34825645a4c 100644
--- a/airflow-core/src/airflow/ui/src/components/StatsCard.tsx
+++ b/airflow-core/src/airflow/ui/src/components/StatsCard.tsx
@@ -17,6 +17,7 @@
* under the License.
*/
import { Box, HStack, Skeleton, Text } from "@chakra-ui/react";
+import type { ReactNode } from "react";
import { FiChevronRight, FiChevronLeft } from "react-icons/fi";
import { Link as RouterLink } from "react-router-dom";
@@ -36,7 +37,7 @@ export const StatsCard = ({
}: {
readonly colorScheme: string;
readonly count: number;
- readonly icon?: React.ReactNode;
+ readonly icon?: ReactNode;
readonly isLoading?: boolean;
readonly isRTL: boolean;
readonly label: string;
diff --git
a/airflow-core/src/airflow/ui/src/components/TriggerDag/TriggerDAGModal.tsx
b/airflow-core/src/airflow/ui/src/components/TriggerDag/TriggerDAGModal.tsx
index 3aa26a29ebe..c70f76ff38c 100644
--- a/airflow-core/src/airflow/ui/src/components/TriggerDag/TriggerDAGModal.tsx
+++ b/airflow-core/src/airflow/ui/src/components/TriggerDag/TriggerDAGModal.tsx
@@ -17,7 +17,7 @@
* under the License.
*/
import { Heading, VStack, HStack, Spinner, Center, Text } from
"@chakra-ui/react";
-import React, { useState } from "react";
+import { useState } from "react";
import { useTranslation } from "react-i18next";
import { useDagServiceGetDag } from "openapi/queries";
@@ -48,14 +48,14 @@ type TriggerDAGModalProps = {
| undefined;
};
-const TriggerDAGModal: React.FC<TriggerDAGModalProps> = ({
+const TriggerDAGModal = ({
dagDisplayName,
dagId,
isPaused,
onClose,
open,
prefillConfig,
-}) => {
+}: TriggerDAGModalProps) => {
const { t: translate } = useTranslation("components");
const [runMode, setRunMode] = useState<RunMode>(RunMode.SINGLE);
const {
diff --git a/airflow-core/src/airflow/ui/src/components/renderStructuredLog.tsx
b/airflow-core/src/airflow/ui/src/components/renderStructuredLog.tsx
index bac81ef906d..bddd70d8a73 100644
--- a/airflow-core/src/airflow/ui/src/components/renderStructuredLog.tsx
+++ b/airflow-core/src/airflow/ui/src/components/renderStructuredLog.tsx
@@ -18,8 +18,8 @@
*/
import { chakra, Code, Link } from "@chakra-ui/react";
import type { TFunction } from "i18next";
-import type { JSX } from "react";
-import * as React from "react";
+import type { JSX, ReactNode } from "react";
+import { Fragment } from "react";
import { Link as RouterLink } from "react-router-dom";
import type { StructuredLogMessage, TaskInstancesLogResponse } from
"openapi/requests/types.gen";
@@ -63,7 +63,7 @@ const addAnsiWithLinks = (line: string) => {
}
let currentIndex = 0;
- const elements: Array<React.ReactNode> = [];
+ const elements: Array<ReactNode> = [];
urlMatches.forEach((match) => {
const { index: startIndex } = match;
@@ -130,12 +130,12 @@ export const renderTIContextPreamble = (
<chakra.span lineHeight={1.5} opacity={0.7}>
{label === undefined ? undefined : <chakra.span
fontWeight="medium">{label}</chakra.span>}
{fields.map((field) => (
- <React.Fragment key={field}>
+ <Fragment key={field}>
{" "}
<span>
<chakra.span
color="fg.info">{field}</chakra.span>={String(context[field])}
</span>
- </React.Fragment>
+ </Fragment>
))}
</chakra.span>
);
@@ -325,7 +325,7 @@ const renderStructuredLogImpl = ({
elements.push(` ${key === "logger" ? "source" :
key}=${stringifiedValue} `);
} else {
elements.push(
- <React.Fragment key={`space_${key}`}> </React.Fragment>,
+ <Fragment key={`space_${key}`}> </Fragment>,
<span data-key={key} key={`struct_${key}`}>
<chakra.span color="fg.info">{key === "logger" ? "source" :
key}</chakra.span>=
<span data-value>{stringifiedValue}</span>
diff --git
a/airflow-core/src/airflow/ui/src/components/ui/ActionBar/BarContent.tsx
b/airflow-core/src/airflow/ui/src/components/ui/ActionBar/BarContent.tsx
index c92e6e107ad..9c2eefc339f 100644
--- a/airflow-core/src/airflow/ui/src/components/ui/ActionBar/BarContent.tsx
+++ b/airflow-core/src/airflow/ui/src/components/ui/ActionBar/BarContent.tsx
@@ -17,11 +17,11 @@
* under the License.
*/
import { ActionBar, Portal } from "@chakra-ui/react";
-import { forwardRef } from "react";
+import { forwardRef, type RefObject } from "react";
type ActionBarContentProps = {
readonly portalled?: boolean;
- readonly portalRef?: React.RefObject<HTMLElement>;
+ readonly portalRef?: RefObject<HTMLElement>;
} & ActionBar.ContentProps;
export const Content = forwardRef<HTMLDivElement,
ActionBarContentProps>((props, ref) => {
diff --git a/airflow-core/src/airflow/ui/src/components/ui/Alert.tsx
b/airflow-core/src/airflow/ui/src/components/ui/Alert.tsx
index ec25eeb4c67..d2c17f34376 100644
--- a/airflow-core/src/airflow/ui/src/components/ui/Alert.tsx
+++ b/airflow-core/src/airflow/ui/src/components/ui/Alert.tsx
@@ -17,15 +17,15 @@
* under the License.
*/
import { Alert as ChakraAlert, CloseButton } from "@chakra-ui/react";
-import { forwardRef } from "react";
+import { forwardRef, type ReactNode, type ReactElement } from "react";
export type AlertProps = {
readonly closable?: boolean;
- readonly endElement?: React.ReactNode;
- readonly icon?: React.ReactElement;
+ readonly endElement?: ReactNode;
+ readonly icon?: ReactElement;
readonly onClose?: () => void;
- readonly startElement?: React.ReactNode;
- readonly title?: React.ReactNode;
+ readonly startElement?: ReactNode;
+ readonly title?: ReactNode;
} & Omit<ChakraAlert.RootProps, "title">;
export const Alert = forwardRef<HTMLDivElement, AlertProps>((props, ref) => {
diff --git a/airflow-core/src/airflow/ui/src/components/ui/Breadcrumb/Root.tsx
b/airflow-core/src/airflow/ui/src/components/ui/Breadcrumb/Root.tsx
index b1b4f81d8aa..652da98d102 100644
--- a/airflow-core/src/airflow/ui/src/components/ui/Breadcrumb/Root.tsx
+++ b/airflow-core/src/airflow/ui/src/components/ui/Breadcrumb/Root.tsx
@@ -17,17 +17,17 @@
* under the License.
*/
import { Breadcrumb, type SystemStyleObject } from "@chakra-ui/react";
-import React from "react";
+import { type ReactNode, forwardRef, Children, isValidElement, Fragment } from
"react";
export type BreadcrumbRootProps = {
- readonly separator?: React.ReactNode;
+ readonly separator?: ReactNode;
readonly separatorGap?: SystemStyleObject["gap"];
} & Breadcrumb.RootProps;
-export const Root = React.forwardRef<HTMLDivElement,
BreadcrumbRootProps>((props, ref) => {
+export const Root = forwardRef<HTMLDivElement, BreadcrumbRootProps>((props,
ref) => {
const { children, separator, separatorGap, ...rest } = props;
- const validChildren =
React.Children.toArray(children).filter(React.isValidElement);
+ const validChildren = Children.toArray(children).filter(isValidElement);
return (
<Breadcrumb.Root ref={ref} {...rest}>
@@ -37,10 +37,10 @@ export const Root = React.forwardRef<HTMLDivElement,
BreadcrumbRootProps>((props
return (
// eslint-disable-next-line react/no-array-index-key
- <React.Fragment key={index}>
+ <Fragment key={index}>
<Breadcrumb.Item>{child}</Breadcrumb.Item>
{!last &&
<Breadcrumb.Separator>{separator}</Breadcrumb.Separator>}
- </React.Fragment>
+ </Fragment>
);
})}
</Breadcrumb.List>
diff --git a/airflow-core/src/airflow/ui/src/components/ui/Checkbox.tsx
b/airflow-core/src/airflow/ui/src/components/ui/Checkbox.tsx
index 1588843131d..1bfd2f52a79 100644
--- a/airflow-core/src/airflow/ui/src/components/ui/Checkbox.tsx
+++ b/airflow-core/src/airflow/ui/src/components/ui/Checkbox.tsx
@@ -17,15 +17,15 @@
* under the License.
*/
import { Checkbox as ChakraCheckbox } from "@chakra-ui/react";
-import * as React from "react";
+import { type ReactNode, type InputHTMLAttributes, type Ref, forwardRef } from
"react";
export type CheckboxProps = {
- readonly icon?: React.ReactNode;
- readonly inputProps?: React.InputHTMLAttributes<HTMLInputElement>;
- readonly rootRef?: React.Ref<HTMLLabelElement>;
+ readonly icon?: ReactNode;
+ readonly inputProps?: InputHTMLAttributes<HTMLInputElement>;
+ readonly rootRef?: Ref<HTMLLabelElement>;
} & ChakraCheckbox.RootProps;
-export const Checkbox = React.forwardRef<HTMLInputElement,
CheckboxProps>((props, ref) => {
+export const Checkbox = forwardRef<HTMLInputElement, CheckboxProps>((props,
ref) => {
const { children, icon, inputProps, rootRef, ...rest } = props;
return (
diff --git a/airflow-core/src/airflow/ui/src/components/ui/Clipboard.tsx
b/airflow-core/src/airflow/ui/src/components/ui/Clipboard.tsx
index 52357446fbe..7afbcc97b9f 100644
--- a/airflow-core/src/airflow/ui/src/components/ui/Clipboard.tsx
+++ b/airflow-core/src/airflow/ui/src/components/ui/Clipboard.tsx
@@ -18,19 +18,19 @@
*/
import type { ButtonProps, InputProps } from "@chakra-ui/react";
import { Button, Clipboard as ChakraClipboard, Input } from "@chakra-ui/react";
-import * as React from "react";
+import { forwardRef } from "react";
import { useTranslation } from "react-i18next";
import { LuCheck, LuClipboard, LuLink } from "react-icons/lu";
import { IconButton } from ".";
-const ClipboardIcon = React.forwardRef<HTMLDivElement,
ChakraClipboard.IndicatorProps>((props, ref) => (
+const ClipboardIcon = forwardRef<HTMLDivElement,
ChakraClipboard.IndicatorProps>((props, ref) => (
<ChakraClipboard.Indicator copied={<LuCheck />} {...props} ref={ref}>
<LuClipboard />
</ChakraClipboard.Indicator>
));
-const ClipboardCopyText = React.forwardRef<HTMLDivElement,
ChakraClipboard.IndicatorProps>((props, ref) => {
+const ClipboardCopyText = forwardRef<HTMLDivElement,
ChakraClipboard.IndicatorProps>((props, ref) => {
const { t: translate } = useTranslation("components");
return (
@@ -40,7 +40,7 @@ const ClipboardCopyText = React.forwardRef<HTMLDivElement,
ChakraClipboard.Indic
);
});
-export const ClipboardLabel = React.forwardRef<HTMLLabelElement,
ChakraClipboard.LabelProps>((props, ref) => (
+export const ClipboardLabel = forwardRef<HTMLLabelElement,
ChakraClipboard.LabelProps>((props, ref) => (
<ChakraClipboard.Label
display="inline-block"
fontWeight="medium"
@@ -51,7 +51,7 @@ export const ClipboardLabel =
React.forwardRef<HTMLLabelElement, ChakraClipboard
/>
));
-export const ClipboardButton = React.forwardRef<HTMLButtonElement,
ButtonProps>((props, ref) => (
+export const ClipboardButton = forwardRef<HTMLButtonElement,
ButtonProps>((props, ref) => (
<ChakraClipboard.Trigger asChild>
<Button ref={ref} variant="outline" {...props}>
<ClipboardIcon />
@@ -60,7 +60,7 @@ export const ClipboardButton =
React.forwardRef<HTMLButtonElement, ButtonProps>(
</ChakraClipboard.Trigger>
));
-export const ClipboardLink = React.forwardRef<HTMLButtonElement,
ButtonProps>((props, ref) => (
+export const ClipboardLink = forwardRef<HTMLButtonElement,
ButtonProps>((props, ref) => (
<ChakraClipboard.Trigger asChild>
<Button
alignItems="center"
@@ -78,7 +78,7 @@ export const ClipboardLink =
React.forwardRef<HTMLButtonElement, ButtonProps>((p
</ChakraClipboard.Trigger>
));
-export const ClipboardIconButton = React.forwardRef<HTMLButtonElement,
ButtonProps>((props, ref) => (
+export const ClipboardIconButton = forwardRef<HTMLButtonElement,
ButtonProps>((props, ref) => (
<ChakraClipboard.Trigger asChild>
<IconButton ref={ref} size="xs" variant="ghost" {...props}>
<ClipboardIcon />
@@ -87,7 +87,7 @@ export const ClipboardIconButton =
React.forwardRef<HTMLButtonElement, ButtonPro
</ChakraClipboard.Trigger>
));
-export const ClipboardInput = React.forwardRef<HTMLInputElement,
InputProps>((props, ref) => (
+export const ClipboardInput = forwardRef<HTMLInputElement, InputProps>((props,
ref) => (
<ChakraClipboard.Input asChild>
<Input ref={ref} {...props} />
</ChakraClipboard.Input>
diff --git a/airflow-core/src/airflow/ui/src/components/ui/Dialog/Content.tsx
b/airflow-core/src/airflow/ui/src/components/ui/Dialog/Content.tsx
index 3d12d5b85ed..1f9d1a52fe9 100644
--- a/airflow-core/src/airflow/ui/src/components/ui/Dialog/Content.tsx
+++ b/airflow-core/src/airflow/ui/src/components/ui/Dialog/Content.tsx
@@ -17,12 +17,12 @@
* under the License.
*/
import { Dialog as ChakraDialog, Portal } from "@chakra-ui/react";
-import { forwardRef } from "react";
+import { forwardRef, type RefObject } from "react";
type ContentProps = {
readonly backdrop?: boolean;
readonly portalled?: boolean;
- readonly portalRef?: React.RefObject<HTMLElement>;
+ readonly portalRef?: RefObject<HTMLElement>;
} & ChakraDialog.ContentProps;
export const Content = forwardRef<HTMLDivElement, ContentProps>((props, ref)
=> {
diff --git a/airflow-core/src/airflow/ui/src/components/ui/InputWithAddon.tsx
b/airflow-core/src/airflow/ui/src/components/ui/InputWithAddon.tsx
index 12123b74082..7c94d6e99db 100644
--- a/airflow-core/src/airflow/ui/src/components/ui/InputWithAddon.tsx
+++ b/airflow-core/src/airflow/ui/src/components/ui/InputWithAddon.tsx
@@ -18,15 +18,15 @@
*/
import type { InputProps } from "@chakra-ui/react";
import { Box, Input, Text } from "@chakra-ui/react";
-import * as React from "react";
+import { type ReactNode, forwardRef } from "react";
export type InputWithAddonProps = {
- readonly endAddon?: React.ReactNode;
+ readonly endAddon?: ReactNode;
readonly label: string;
readonly width?: string;
} & InputProps;
-export const InputWithAddon = React.forwardRef<HTMLInputElement,
InputWithAddonProps>((props, ref) => {
+export const InputWithAddon = forwardRef<HTMLInputElement,
InputWithAddonProps>((props, ref) => {
const { endAddon, label, width = "220px", ...inputProps } = props;
return (
diff --git a/airflow-core/src/airflow/ui/src/components/ui/LazyClipboard.tsx
b/airflow-core/src/airflow/ui/src/components/ui/LazyClipboard.tsx
index 67a727ff136..fb21841bc84 100644
--- a/airflow-core/src/airflow/ui/src/components/ui/LazyClipboard.tsx
+++ b/airflow-core/src/airflow/ui/src/components/ui/LazyClipboard.tsx
@@ -17,7 +17,7 @@
* under the License.
*/
import type { ButtonProps } from "@chakra-ui/react";
-import * as React from "react";
+import { forwardRef, useState } from "react";
import { LuCheck, LuClipboard } from "react-icons/lu";
import { IconButton } from ".";
@@ -28,9 +28,9 @@ type LazyClipboardProps = {
} & ButtonProps;
/** Clipboard button that lazily computes the value only when clicked */
-export const LazyClipboard = React.forwardRef<HTMLButtonElement,
LazyClipboardProps>(
+export const LazyClipboard = forwardRef<HTMLButtonElement, LazyClipboardProps>(
({ getValue, label, ...props }, ref) => {
- const [copied, setCopied] = React.useState(false);
+ const [copied, setCopied] = useState(false);
const handleClick = () => {
const value = getValue();
diff --git a/airflow-core/src/airflow/ui/src/components/ui/Menu/MenuContent.tsx
b/airflow-core/src/airflow/ui/src/components/ui/Menu/MenuContent.tsx
index 3ff93b98144..19441797bfa 100644
--- a/airflow-core/src/airflow/ui/src/components/ui/Menu/MenuContent.tsx
+++ b/airflow-core/src/airflow/ui/src/components/ui/Menu/MenuContent.tsx
@@ -17,11 +17,11 @@
* under the License.
*/
import { Menu as ChakraMenu, Portal } from "@chakra-ui/react";
-import { forwardRef } from "react";
+import { forwardRef, type RefObject } from "react";
type MenuContentProps = {
readonly portalled?: boolean;
- readonly portalRef?: React.RefObject<HTMLElement>;
+ readonly portalRef?: RefObject<HTMLElement>;
} & ChakraMenu.ContentProps;
export const Content = forwardRef<HTMLDivElement, MenuContentProps>((props,
ref) => {
diff --git a/airflow-core/src/airflow/ui/src/components/ui/NumberInput.tsx
b/airflow-core/src/airflow/ui/src/components/ui/NumberInput.tsx
index 3a6ed3ef48c..49858274443 100644
--- a/airflow-core/src/airflow/ui/src/components/ui/NumberInput.tsx
+++ b/airflow-core/src/airflow/ui/src/components/ui/NumberInput.tsx
@@ -17,11 +17,11 @@
* under the License.
*/
import { NumberInput as ChakraNumberInput } from "@chakra-ui/react";
-import * as React from "react";
+import { forwardRef } from "react";
export type NumberInputProps = {} & ChakraNumberInput.RootProps;
-export const NumberInputRoot = React.forwardRef<HTMLDivElement,
NumberInputProps>((props, ref) => {
+export const NumberInputRoot = forwardRef<HTMLDivElement,
NumberInputProps>((props, ref) => {
const { children, ...rest } = props;
return (
diff --git a/airflow-core/src/airflow/ui/src/components/ui/Pagination/Items.tsx
b/airflow-core/src/airflow/ui/src/components/ui/Pagination/Items.tsx
index e72e9ad099e..68b91d1b1e4 100644
--- a/airflow-core/src/airflow/ui/src/components/ui/Pagination/Items.tsx
+++ b/airflow-core/src/airflow/ui/src/components/ui/Pagination/Items.tsx
@@ -17,11 +17,12 @@
* under the License.
*/
import { Pagination as ChakraPagination } from "@chakra-ui/react";
+import type { HTMLAttributes } from "react";
import { Ellipsis } from "./Ellipsis";
import { Item } from "./Item";
-export const Items = (props: React.HTMLAttributes<HTMLElement>) => (
+export const Items = (props: HTMLAttributes<HTMLElement>) => (
<ChakraPagination.Context>
{({ pages }) =>
pages.map((page, index) =>
diff --git a/airflow-core/src/airflow/ui/src/components/ui/Popover/Arrow.tsx
b/airflow-core/src/airflow/ui/src/components/ui/Popover/Arrow.tsx
index ebf6da55066..ec216c24381 100644
--- a/airflow-core/src/airflow/ui/src/components/ui/Popover/Arrow.tsx
+++ b/airflow-core/src/airflow/ui/src/components/ui/Popover/Arrow.tsx
@@ -17,9 +17,9 @@
* under the License.
*/
import { Popover as ChakraPopover } from "@chakra-ui/react";
-import * as React from "react";
+import { forwardRef } from "react";
-export const Arrow = React.forwardRef<HTMLDivElement,
ChakraPopover.ArrowProps>((props, ref) => (
+export const Arrow = forwardRef<HTMLDivElement,
ChakraPopover.ArrowProps>((props, ref) => (
<ChakraPopover.Arrow {...props} ref={ref}>
<ChakraPopover.ArrowTip />
</ChakraPopover.Arrow>
diff --git
a/airflow-core/src/airflow/ui/src/components/ui/Popover/CloseTrigger.tsx
b/airflow-core/src/airflow/ui/src/components/ui/Popover/CloseTrigger.tsx
index 9416bb995f1..eb11b272928 100644
--- a/airflow-core/src/airflow/ui/src/components/ui/Popover/CloseTrigger.tsx
+++ b/airflow-core/src/airflow/ui/src/components/ui/Popover/CloseTrigger.tsx
@@ -17,12 +17,10 @@
* under the License.
*/
import { CloseButton, Popover as ChakraPopover } from "@chakra-ui/react";
-import * as React from "react";
+import { forwardRef } from "react";
-export const CloseTrigger = React.forwardRef<HTMLButtonElement,
ChakraPopover.CloseTriggerProps>(
- (props, ref) => (
- <ChakraPopover.CloseTrigger insetEnd="1" position="absolute" top="1"
{...props} asChild ref={ref}>
- <CloseButton size="sm" />
- </ChakraPopover.CloseTrigger>
- ),
-);
+export const CloseTrigger = forwardRef<HTMLButtonElement,
ChakraPopover.CloseTriggerProps>((props, ref) => (
+ <ChakraPopover.CloseTrigger insetEnd="1" position="absolute" top="1"
{...props} asChild ref={ref}>
+ <CloseButton size="sm" />
+ </ChakraPopover.CloseTrigger>
+));
diff --git a/airflow-core/src/airflow/ui/src/components/ui/Popover/Content.tsx
b/airflow-core/src/airflow/ui/src/components/ui/Popover/Content.tsx
index 4695251534f..e4d82b91d5c 100644
--- a/airflow-core/src/airflow/ui/src/components/ui/Popover/Content.tsx
+++ b/airflow-core/src/airflow/ui/src/components/ui/Popover/Content.tsx
@@ -17,14 +17,14 @@
* under the License.
*/
import { Popover as ChakraPopover, Portal } from "@chakra-ui/react";
-import * as React from "react";
+import { type RefObject, forwardRef } from "react";
type PopoverContentProps = {
readonly portalled?: boolean;
- readonly portalRef?: React.RefObject<HTMLElement>;
+ readonly portalRef?: RefObject<HTMLElement>;
} & ChakraPopover.ContentProps;
-export const Content = React.forwardRef<HTMLDivElement,
PopoverContentProps>((props, ref) => {
+export const Content = forwardRef<HTMLDivElement, PopoverContentProps>((props,
ref) => {
const { portalled = true, portalRef, ...rest } = props;
return (
diff --git a/airflow-core/src/airflow/ui/src/components/ui/RadioCard.tsx
b/airflow-core/src/airflow/ui/src/components/ui/RadioCard.tsx
index 22f7059aa67..18fa8c3be2f 100644
--- a/airflow-core/src/airflow/ui/src/components/ui/RadioCard.tsx
+++ b/airflow-core/src/airflow/ui/src/components/ui/RadioCard.tsx
@@ -17,12 +17,12 @@
* under the License.
*/
import { RadioCard } from "@chakra-ui/react";
-import { forwardRef } from "react";
+import { forwardRef, type ReactNode } from "react";
type RadioCardItemProps = {
- readonly description?: React.ReactNode;
+ readonly description?: ReactNode;
readonly indicatorPlacement?: "end" | "start";
- readonly label?: React.ReactNode;
+ readonly label?: ReactNode;
} & RadioCard.ItemProps;
export const RadioCardItem = forwardRef<HTMLInputElement,
RadioCardItemProps>((props, ref) => {
diff --git a/airflow-core/src/airflow/ui/src/components/ui/Select/Content.tsx
b/airflow-core/src/airflow/ui/src/components/ui/Select/Content.tsx
index 81a30e84db5..62e2ae6ae52 100644
--- a/airflow-core/src/airflow/ui/src/components/ui/Select/Content.tsx
+++ b/airflow-core/src/airflow/ui/src/components/ui/Select/Content.tsx
@@ -17,11 +17,11 @@
* under the License.
*/
import { Select as ChakraSelect, Portal } from "@chakra-ui/react";
-import { forwardRef } from "react";
+import { forwardRef, type RefObject } from "react";
type ContentProps = {
readonly portalled?: boolean;
- readonly portalRef?: React.RefObject<HTMLElement | null>;
+ readonly portalRef?: RefObject<HTMLElement | null>;
} & ChakraSelect.ContentProps;
export const Content = forwardRef<HTMLDivElement, ContentProps>((props, ref)
=> {
diff --git a/airflow-core/src/airflow/ui/src/components/ui/Select/ItemGroup.tsx
b/airflow-core/src/airflow/ui/src/components/ui/Select/ItemGroup.tsx
index 826b3805d8b..d38c0cc96bb 100644
--- a/airflow-core/src/airflow/ui/src/components/ui/Select/ItemGroup.tsx
+++ b/airflow-core/src/airflow/ui/src/components/ui/Select/ItemGroup.tsx
@@ -17,10 +17,10 @@
* under the License.
*/
import { Select as ChakraSelect } from "@chakra-ui/react";
-import { forwardRef } from "react";
+import { forwardRef, type ReactNode } from "react";
type ItemGroupProps = {
- readonly label: React.ReactNode;
+ readonly label: ReactNode;
} & ChakraSelect.ItemGroupProps;
export const ItemGroup = forwardRef<HTMLDivElement, ItemGroupProps>((props,
ref) => {
diff --git a/airflow-core/src/airflow/ui/src/components/ui/Select/ValueText.tsx
b/airflow-core/src/airflow/ui/src/components/ui/Select/ValueText.tsx
index 72b47b89f06..ba57651263c 100644
--- a/airflow-core/src/airflow/ui/src/components/ui/Select/ValueText.tsx
+++ b/airflow-core/src/airflow/ui/src/components/ui/Select/ValueText.tsx
@@ -18,10 +18,10 @@
*/
import type { CollectionItem } from "@chakra-ui/react";
import { Select as ChakraSelect } from "@chakra-ui/react";
-import { forwardRef } from "react";
+import { forwardRef, type ReactNode } from "react";
type ValueTextProps = {
- readonly children?: (items: Array<CollectionItem>) => React.ReactNode;
+ readonly children?: (items: Array<CollectionItem>) => ReactNode;
} & Omit<ChakraSelect.ValueTextProps, "children">;
export const ValueText = forwardRef<HTMLSpanElement, ValueTextProps>((props,
ref) => {
diff --git a/airflow-core/src/airflow/ui/src/components/ui/Switch.tsx
b/airflow-core/src/airflow/ui/src/components/ui/Switch.tsx
index c949cc64f0f..0a876b7f58c 100644
--- a/airflow-core/src/airflow/ui/src/components/ui/Switch.tsx
+++ b/airflow-core/src/airflow/ui/src/components/ui/Switch.tsx
@@ -17,13 +17,13 @@
* under the License.
*/
import { Switch as ChakraSwitch } from "@chakra-ui/react";
-import { forwardRef } from "react";
+import { forwardRef, type InputHTMLAttributes, type Ref, type ReactNode } from
"react";
export type SwitchProps = {
- readonly inputProps?: React.InputHTMLAttributes<HTMLInputElement>;
- readonly rootRef?: React.Ref<HTMLLabelElement>;
- readonly thumbLabel?: { off: React.ReactNode; on: React.ReactNode };
- readonly trackLabel?: { off: React.ReactNode; on: React.ReactNode };
+ readonly inputProps?: InputHTMLAttributes<HTMLInputElement>;
+ readonly rootRef?: Ref<HTMLLabelElement>;
+ readonly thumbLabel?: { off: ReactNode; on: ReactNode };
+ readonly trackLabel?: { off: ReactNode; on: ReactNode };
} & ChakraSwitch.RootProps;
export const Switch = forwardRef<HTMLInputElement, SwitchProps>((props, ref)
=> {
diff --git a/airflow-core/src/airflow/ui/src/components/ui/Tag.tsx
b/airflow-core/src/airflow/ui/src/components/ui/Tag.tsx
index 0a432b05480..ea5daa5c5ad 100644
--- a/airflow-core/src/airflow/ui/src/components/ui/Tag.tsx
+++ b/airflow-core/src/airflow/ui/src/components/ui/Tag.tsx
@@ -17,13 +17,13 @@
* under the License.
*/
import { Tag as ChakraTag } from "@chakra-ui/react";
-import { forwardRef } from "react";
+import { forwardRef, type ReactNode } from "react";
export type TagProps = {
readonly closable?: boolean;
- readonly endElement?: React.ReactNode;
+ readonly endElement?: ReactNode;
readonly onClose?: VoidFunction;
- readonly startElement?: React.ReactNode;
+ readonly startElement?: ReactNode;
} & ChakraTag.RootProps;
export const Tag = forwardRef<HTMLSpanElement, TagProps>((props, ref) => {
diff --git a/airflow-core/src/airflow/ui/src/components/ui/Tooltip.tsx
b/airflow-core/src/airflow/ui/src/components/ui/Tooltip.tsx
index 9bba93be468..f433c944028 100644
--- a/airflow-core/src/airflow/ui/src/components/ui/Tooltip.tsx
+++ b/airflow-core/src/airflow/ui/src/components/ui/Tooltip.tsx
@@ -17,18 +17,18 @@
* under the License.
*/
import { Tooltip as ChakraTooltip, Portal } from "@chakra-ui/react";
-import * as React from "react";
+import { type ReactNode, type RefObject, forwardRef } from "react";
export type TooltipProps = {
- readonly content: React.ReactNode;
+ readonly content: ReactNode;
readonly contentProps?: ChakraTooltip.ContentProps;
readonly disabled?: boolean;
readonly portalled?: boolean;
- readonly portalRef?: React.RefObject<HTMLElement>;
+ readonly portalRef?: RefObject<HTMLElement>;
readonly showArrow?: boolean;
} & ChakraTooltip.RootProps;
-export const Tooltip = React.forwardRef<HTMLDivElement, TooltipProps>((props,
ref) => {
+export const Tooltip = forwardRef<HTMLDivElement, TooltipProps>((props, ref)
=> {
const {
children,
content,
diff --git a/airflow-core/src/airflow/ui/src/hooks/useDateRangeFilter.ts
b/airflow-core/src/airflow/ui/src/hooks/useDateRangeFilter.ts
index a184204b476..b5bfdbb35f9 100644
--- a/airflow-core/src/airflow/ui/src/hooks/useDateRangeFilter.ts
+++ b/airflow-core/src/airflow/ui/src/hooks/useDateRangeFilter.ts
@@ -19,7 +19,7 @@
import dayjs from "dayjs";
import timezone from "dayjs/plugin/timezone";
import type { TFunction } from "i18next";
-import { useEffect, useState } from "react";
+import { useEffect, useState, type ChangeEvent } from "react";
import type { DateRangeValue } from "src/components/FilterBar/types";
import { isValidDateValue } from "src/components/FilterBar/utils";
@@ -252,7 +252,7 @@ export const useDateRangeFilter = ({ onChange, translate,
value }: UseDateRangeF
};
const handleInputChange =
- (field: "end" | "start", inputType: "date" | "time") => (event:
React.ChangeEvent<HTMLInputElement>) => {
+ (field: "end" | "start", inputType: "date" | "time") => (event:
ChangeEvent<HTMLInputElement>) => {
const inputValue = event.target.value;
const inputKey = inputType === "date" ? field : (`${field}Time` as
const);
diff --git a/airflow-core/src/airflow/ui/src/hooks/useRequiredActionTabs.ts
b/airflow-core/src/airflow/ui/src/hooks/useRequiredActionTabs.ts
index 0560327fac3..e5160abba12 100644
--- a/airflow-core/src/airflow/ui/src/hooks/useRequiredActionTabs.ts
+++ b/airflow-core/src/airflow/ui/src/hooks/useRequiredActionTabs.ts
@@ -16,7 +16,7 @@
* specific language governing permissions and limitations
* under the License.
*/
-import { useEffect } from "react";
+import { useEffect, type ReactNode } from "react";
import { useTranslation } from "react-i18next";
import { useLocation, useNavigate } from "react-router-dom";
@@ -30,7 +30,7 @@ export type HITLQueryParams = {
};
export type TabItem = {
- icon: React.ReactNode;
+ icon: ReactNode;
label: string;
value: string;
};
diff --git a/airflow-core/src/airflow/ui/src/layouts/Details/PanelButtons.tsx
b/airflow-core/src/airflow/ui/src/layouts/Details/PanelButtons.tsx
index 8d8fa30757d..00373519900 100644
--- a/airflow-core/src/airflow/ui/src/layouts/Details/PanelButtons.tsx
+++ b/airflow-core/src/airflow/ui/src/layouts/Details/PanelButtons.tsx
@@ -27,7 +27,7 @@ import {
VStack,
} from "@chakra-ui/react";
import { useReactFlow } from "@xyflow/react";
-import { useEffect, useRef } from "react";
+import { useEffect, useRef, type RefObject, type Dispatch, type SetStateAction
} from "react";
import { useTranslation } from "react-i18next";
import { FiGrid } from "react-icons/fi";
import { LuChartGantt } from "react-icons/lu";
@@ -58,10 +58,10 @@ import { VersionIndicatorSelect } from
"./VersionIndicatorSelect";
type Props = {
readonly dagView: DagView;
readonly limit: number;
- readonly panelGroupRef: React.RefObject<ImperativePanelGroupHandle | null>;
+ readonly panelGroupRef: RefObject<ImperativePanelGroupHandle | null>;
readonly setDagView: (value: DagView) => void;
readonly setLimit: (value: number) => void;
- readonly setShowVersionIndicatorMode:
React.Dispatch<React.SetStateAction<VersionIndicatorOptions>>;
+ readonly setShowVersionIndicatorMode:
Dispatch<SetStateAction<VersionIndicatorOptions>>;
readonly showVersionIndicatorMode: VersionIndicatorOptions;
};
diff --git a/airflow-core/src/airflow/ui/src/layouts/Nav/LanguageModal.tsx
b/airflow-core/src/airflow/ui/src/layouts/Nav/LanguageModal.tsx
index 0815783f1ad..bd5e3b551a9 100644
--- a/airflow-core/src/airflow/ui/src/layouts/Nav/LanguageModal.tsx
+++ b/airflow-core/src/airflow/ui/src/layouts/Nav/LanguageModal.tsx
@@ -16,7 +16,6 @@
* specific language governing permissions and limitations
* under the License.
*/
-import React from "react";
import { useTranslation } from "react-i18next";
import { Dialog } from "src/components/ui";
@@ -28,7 +27,7 @@ type LanguageModalProps = {
readonly onClose: () => void;
};
-const LanguageModal: React.FC<LanguageModalProps> = ({ isOpen, onClose }) => {
+const LanguageModal = ({ isOpen, onClose }: LanguageModalProps) => {
const { t: translate } = useTranslation();
return (
diff --git a/airflow-core/src/airflow/ui/src/layouts/Nav/LanguageSelector.tsx
b/airflow-core/src/airflow/ui/src/layouts/Nav/LanguageSelector.tsx
index e4581aebc7e..6952c156d25 100644
--- a/airflow-core/src/airflow/ui/src/layouts/Nav/LanguageSelector.tsx
+++ b/airflow-core/src/airflow/ui/src/layouts/Nav/LanguageSelector.tsx
@@ -18,12 +18,12 @@
*/
import { Field, VStack, Box, Text } from "@chakra-ui/react";
import { Select, type SingleValue } from "chakra-react-select";
-import React, { useState, useCallback } from "react";
+import { useState, useCallback } from "react";
import { useTranslation } from "react-i18next";
import { supportedLanguages } from "src/i18n/config";
-const LanguageSelector: React.FC = () => {
+const LanguageSelector = () => {
const { i18n, t: translate } = useTranslation();
const [selectedLang, setSelectedLang] = useState(i18n.resolvedLanguage ??
i18n.language);
diff --git a/airflow-core/src/airflow/ui/src/layouts/Nav/LogoutModal.tsx
b/airflow-core/src/airflow/ui/src/layouts/Nav/LogoutModal.tsx
index 6ad8226c22b..b999409229d 100644
--- a/airflow-core/src/airflow/ui/src/layouts/Nav/LogoutModal.tsx
+++ b/airflow-core/src/airflow/ui/src/layouts/Nav/LogoutModal.tsx
@@ -17,7 +17,6 @@
* under the License.
*/
import { Text } from "@chakra-ui/react";
-import React from "react";
import { useTranslation } from "react-i18next";
import { ConfirmationModal } from "src/components/ConfirmationModal";
@@ -28,7 +27,7 @@ type LogoutModalProps = {
readonly onClose: () => void;
};
-const LogoutModal: React.FC<LogoutModalProps> = ({ isOpen, onClose }) => {
+const LogoutModal = ({ isOpen, onClose }: LogoutModalProps) => {
const { t: translate } = useTranslation("common");
return (
diff --git a/airflow-core/src/airflow/ui/src/layouts/Nav/TimezoneModal.tsx
b/airflow-core/src/airflow/ui/src/layouts/Nav/TimezoneModal.tsx
index fed82207e05..f8a11272ab4 100644
--- a/airflow-core/src/airflow/ui/src/layouts/Nav/TimezoneModal.tsx
+++ b/airflow-core/src/airflow/ui/src/layouts/Nav/TimezoneModal.tsx
@@ -16,7 +16,6 @@
* specific language governing permissions and limitations
* under the License.
*/
-import React from "react";
import { useTranslation } from "react-i18next";
import { Dialog } from "src/components/ui";
@@ -28,7 +27,7 @@ type TimezoneModalProps = {
readonly onClose: () => void;
};
-const TimezoneModal: React.FC<TimezoneModalProps> = ({ isOpen, onClose }) => {
+const TimezoneModal = ({ isOpen, onClose }: TimezoneModalProps) => {
const { t: translate } = useTranslation("common");
return (
diff --git a/airflow-core/src/airflow/ui/src/layouts/Nav/TimezoneSelector.tsx
b/airflow-core/src/airflow/ui/src/layouts/Nav/TimezoneSelector.tsx
index b2aa2dae511..747ec638c69 100644
--- a/airflow-core/src/airflow/ui/src/layouts/Nav/TimezoneSelector.tsx
+++ b/airflow-core/src/airflow/ui/src/layouts/Nav/TimezoneSelector.tsx
@@ -21,7 +21,7 @@ import { Select, type SingleValue } from
"chakra-react-select";
import dayjs from "dayjs";
import timezone from "dayjs/plugin/timezone";
import utc from "dayjs/plugin/utc";
-import React, { useEffect, useState } from "react";
+import { useEffect, useState } from "react";
import { useTranslation } from "react-i18next";
import { useTimezone } from "src/context/timezone";
@@ -31,7 +31,7 @@ import type { Option as TimezoneOption } from
"src/utils/option";
dayjs.extend(utc);
dayjs.extend(timezone);
-const TimezoneSelector: React.FC = () => {
+const TimezoneSelector = () => {
const { selectedTimezone, setSelectedTimezone } = useTimezone();
const { t: translate } = useTranslation("common");
const [currentTime, setCurrentTime] = useState<string>("");
diff --git
a/airflow-core/src/airflow/ui/src/layouts/Nav/TokenGenerationModal.tsx
b/airflow-core/src/airflow/ui/src/layouts/Nav/TokenGenerationModal.tsx
index bf52a4e1e30..099ed202c40 100644
--- a/airflow-core/src/airflow/ui/src/layouts/Nav/TokenGenerationModal.tsx
+++ b/airflow-core/src/airflow/ui/src/layouts/Nav/TokenGenerationModal.tsx
@@ -17,7 +17,7 @@
* under the License.
*/
import { Box, Button, Flex, HStack, Text } from "@chakra-ui/react";
-import React, { useCallback, useState } from "react";
+import { useCallback, useState } from "react";
import { useTranslation } from "react-i18next";
import { FiAlertTriangle } from "react-icons/fi";
@@ -46,7 +46,7 @@ const formatExpiration = (seconds: number): string => {
return `${String(minutes)}m`;
};
-const TokenGenerationModal: React.FC<TokenGenerationModalProps> = ({ isOpen,
onClose }) => {
+const TokenGenerationModal = ({ isOpen, onClose }: TokenGenerationModalProps)
=> {
const { t: translate } = useTranslation();
const [tokenType, setTokenType] = useState<TokenType>("api");
const [generatedToken, setGeneratedToken] = useState<string>();
diff --git a/airflow-core/src/airflow/ui/src/main.tsx
b/airflow-core/src/airflow/ui/src/main.tsx
index a88f22a0084..b721f9053b2 100644
--- a/airflow-core/src/airflow/ui/src/main.tsx
+++ b/airflow-core/src/airflow/ui/src/main.tsx
@@ -20,6 +20,9 @@ import * as ChakraUI from "@chakra-ui/react";
import * as EmotionReact from "@emotion/react";
import { QueryClientProvider } from "@tanstack/react-query";
import axios, { type AxiosError } from "axios";
+// Plugins are UMD-bundled against a `React` global, so this republishes the
+// host's React to them verbatim — the one place the namespace import is
needed.
+// eslint-disable-next-line @typescript-eslint/no-restricted-imports
import React, { StrictMode } from "react";
import * as ReactDOM from "react-dom";
import { createRoot } from "react-dom/client";
diff --git a/airflow-core/src/airflow/ui/src/pages/Asset/AssetPanelButtons.tsx
b/airflow-core/src/airflow/ui/src/pages/Asset/AssetPanelButtons.tsx
index ae40a29255c..2eccf1e7157 100644
--- a/airflow-core/src/airflow/ui/src/pages/Asset/AssetPanelButtons.tsx
+++ b/airflow-core/src/airflow/ui/src/pages/Asset/AssetPanelButtons.tsx
@@ -17,6 +17,7 @@
* under the License.
*/
import { Box, Button, ButtonGroup, Flex, Popover, Portal } from
"@chakra-ui/react";
+import type { Dispatch, SetStateAction } from "react";
import { useTranslation } from "react-i18next";
import { MdSettings } from "react-icons/md";
import { useParams } from "react-router-dom";
@@ -26,7 +27,7 @@ import { IconButton } from "src/components/ui";
type Props = {
readonly dependencyType: "data" | "scheduling";
- readonly setDependencyType: React.Dispatch<React.SetStateAction<"data" |
"scheduling">>;
+ readonly setDependencyType: Dispatch<SetStateAction<"data" | "scheduling">>;
};
export const AssetPanelButtons = ({ dependencyType, setDependencyType }:
Props) => {
diff --git
a/airflow-core/src/airflow/ui/src/pages/Dashboard/Stats/DagImportErrorsModal.tsx
b/airflow-core/src/airflow/ui/src/pages/Dashboard/Stats/DagImportErrorsModal.tsx
index 61c0bd57bc4..ccb60b8594d 100644
---
a/airflow-core/src/airflow/ui/src/pages/Dashboard/Stats/DagImportErrorsModal.tsx
+++
b/airflow-core/src/airflow/ui/src/pages/Dashboard/Stats/DagImportErrorsModal.tsx
@@ -29,13 +29,13 @@ import { Accordion, ClipboardIconButton, Dialog } from
"src/components/ui";
import { Pagination } from "src/components/ui/Pagination";
type ImportDAGErrorModalProps = {
- onClose: () => void;
- open: boolean;
+ readonly onClose: () => void;
+ readonly open: boolean;
};
const PAGE_LIMIT = 15;
-export const DagImportErrorsModal: React.FC<ImportDAGErrorModalProps> = ({
onClose, open }) => {
+export const DagImportErrorsModal = ({ onClose, open }:
ImportDAGErrorModalProps) => {
const [page, setPage] = useState(1);
const [searchQuery, setSearchQuery] = useState("");
diff --git
a/airflow-core/src/airflow/ui/src/pages/Dashboard/Stats/PluginImportErrorsModal.tsx
b/airflow-core/src/airflow/ui/src/pages/Dashboard/Stats/PluginImportErrorsModal.tsx
index 99ee6934ed6..03efc2454f1 100644
---
a/airflow-core/src/airflow/ui/src/pages/Dashboard/Stats/PluginImportErrorsModal.tsx
+++
b/airflow-core/src/airflow/ui/src/pages/Dashboard/Stats/PluginImportErrorsModal.tsx
@@ -28,18 +28,14 @@ import { Accordion, ClipboardIconButton, Dialog } from
"src/components/ui";
import { Pagination } from "src/components/ui/Pagination";
type PluginImportErrorsModalProps = {
- importErrors: Array<PluginImportErrorResponse>;
- onClose: () => void;
- open: boolean;
+ readonly importErrors: Array<PluginImportErrorResponse>;
+ readonly onClose: () => void;
+ readonly open: boolean;
};
const PAGE_LIMIT = 15;
-export const PluginImportErrorsModal: React.FC<PluginImportErrorsModalProps> =
({
- importErrors,
- onClose,
- open,
-}) => {
+export const PluginImportErrorsModal = ({ importErrors, onClose, open }:
PluginImportErrorsModalProps) => {
const { t: translate } = useTranslation("admin");
const [page, setPage] = useState(1);
const [searchQuery, setSearchQuery] = useState("");
diff --git
a/airflow-core/src/airflow/ui/src/queries/useGridTISummaries.test.tsx
b/airflow-core/src/airflow/ui/src/queries/useGridTISummaries.test.tsx
index 50cbd3c7d93..3f385c2909f 100644
--- a/airflow-core/src/airflow/ui/src/queries/useGridTISummaries.test.tsx
+++ b/airflow-core/src/airflow/ui/src/queries/useGridTISummaries.test.tsx
@@ -18,7 +18,7 @@
*/
import { focusManager, QueryClient, QueryClientProvider } from
"@tanstack/react-query";
import { act, renderHook } from "@testing-library/react";
-import React from "react";
+import type { ReactNode } from "react";
import { afterEach, beforeEach, describe, expect, it, vi, type Mock } from
"vitest";
import {
@@ -60,7 +60,7 @@ const createMockResponse = (chunks: Array<string>) => {
const createWrapper =
(queryClient: QueryClient) =>
- ({ children }: { readonly children: React.ReactNode }) => (
+ ({ children }: { readonly children: ReactNode }) => (
<QueryClientProvider client={queryClient}>{children}</QueryClientProvider>
);
diff --git a/airflow-core/src/airflow/ui/src/queries/useTogglePause.test.tsx
b/airflow-core/src/airflow/ui/src/queries/useTogglePause.test.tsx
index 47fa20849bf..79b459594e7 100644
--- a/airflow-core/src/airflow/ui/src/queries/useTogglePause.test.tsx
+++ b/airflow-core/src/airflow/ui/src/queries/useTogglePause.test.tsx
@@ -20,7 +20,7 @@ import { QueryClient, QueryClientProvider } from
"@tanstack/react-query";
import { renderHook, waitFor } from "@testing-library/react";
import { http, HttpResponse } from "msw";
import { setupServer } from "msw/node";
-import React from "react";
+import type { ReactNode } from "react";
import { afterAll, afterEach, beforeAll, describe, expect, it } from "vitest";
import { UseDagServiceGetDagsUiKeyFn } from "openapi/queries";
@@ -71,7 +71,7 @@ const server = setupServer();
const createWrapper =
(queryClient: QueryClient) =>
- ({ children }: { readonly children: React.ReactNode }) => (
+ ({ children }: { readonly children: ReactNode }) => (
<QueryClientProvider client={queryClient}>{children}</QueryClientProvider>
);
diff --git a/dev/react-plugin-tools/react_plugin_template/eslint.config.js
b/dev/react-plugin-tools/react_plugin_template/eslint.config.js
index 57b4176ed9f..71f6aac62fd 100644
--- a/dev/react-plugin-tools/react_plugin_template/eslint.config.js
+++ b/dev/react-plugin-tools/react_plugin_template/eslint.config.js
@@ -41,6 +41,40 @@ export default tseslint.config(
},
rules: {
...reactHooks.configs.recommended.rules,
+ // `@types/react` declares `export as namespace React`, so
`React.ReactNode`
+ // type-checks even without an import — only a syntax rule catches those.
+ "@typescript-eslint/no-restricted-imports": [
+ "error",
+ {
+ paths: [
+ {
+ importNames: ["default"],
+ message:
+ 'The automatic JSX runtime makes the React namespace import
unnecessary. Import what you need by name, e.g. `import { forwardRef, type
ReactNode } from "react"`.',
+ name: "react",
+ },
+ ],
+ },
+ ],
+ "no-restricted-syntax": [
+ "error",
+ {
+ message:
+ "Do not type components with `React.FC`. Annotate the props
parameter instead: `const Foo = ({ bar }: FooProps) => ...`.",
+ selector:
"TSQualifiedName[left.name='React'][right.name=/^(FC|FunctionComponent|VFC)$/]",
+ },
+ {
+ message:
+ "Do not type components with `FC`. Annotate the props parameter
instead: `const Foo = ({ bar }: FooProps) => ...`.",
+ selector:
+ "VariableDeclarator[init.type='ArrowFunctionExpression'] >
Identifier > TSTypeAnnotation > TSTypeReference >
Identifier[name=/^(FC|FunctionComponent|VFC)$/]",
+ },
+ {
+ message:
+ 'Import React types by name instead of qualifying them with the
`React` namespace: `import { type ReactElement } from "react"`, then use
`ReactElement`.',
+ selector:
"TSQualifiedName[left.name='React']:not([right.name=/^(FC|FunctionComponent|VFC)$/])",
+ },
+ ],
"perfectionist/sort-objects": [
"error",
{
diff --git a/dev/react-plugin-tools/react_plugin_template/src/main.tsx
b/dev/react-plugin-tools/react_plugin_template/src/main.tsx
index a3fb8dec9f7..c7a54819673 100644
--- a/dev/react-plugin-tools/react_plugin_template/src/main.tsx
+++ b/dev/react-plugin-tools/react_plugin_template/src/main.tsx
@@ -18,7 +18,6 @@
*/
import { ChakraProvider } from "@chakra-ui/react";
-import { FC } from "react";
import { ColorModeProvider } from "src/context/colorMode";
import { HomePage } from "src/pages/HomePage";
@@ -32,7 +31,7 @@ export interface PluginComponentProps {
/**
* Main plugin component
*/
-const PluginComponent: FC<PluginComponentProps> = (props) => {
+const PluginComponent = (props: PluginComponentProps) => {
// Use the globalChakraUISystem provided by the Airflow Core UI,
// so the plugin has a consistent theming with the host Airflow UI,
diff --git
a/providers/edge3/src/airflow/providers/edge3/plugins/www/eslint.config.js
b/providers/edge3/src/airflow/providers/edge3/plugins/www/eslint.config.js
index 2148a779968..dc5be0988a3 100644
--- a/providers/edge3/src/airflow/providers/edge3/plugins/www/eslint.config.js
+++ b/providers/edge3/src/airflow/providers/edge3/plugins/www/eslint.config.js
@@ -40,6 +40,40 @@ export default tseslint.config(
},
rules: {
...reactHooks.configs.recommended.rules,
+ // `@types/react` declares `export as namespace React`, so
`React.ReactNode`
+ // type-checks even without an import — only a syntax rule catches those.
+ "@typescript-eslint/no-restricted-imports": [
+ "error",
+ {
+ paths: [
+ {
+ importNames: ["default"],
+ message:
+ 'The automatic JSX runtime makes the React namespace import
unnecessary. Import what you need by name, e.g. `import { forwardRef, type
ReactNode } from "react"`.',
+ name: "react",
+ },
+ ],
+ },
+ ],
+ "no-restricted-syntax": [
+ "error",
+ {
+ message:
+ "Do not type components with `React.FC`. Annotate the props
parameter instead: `const Foo = ({ bar }: FooProps) => ...`.",
+ selector:
"TSQualifiedName[left.name='React'][right.name=/^(FC|FunctionComponent|VFC)$/]",
+ },
+ {
+ message:
+ "Do not type components with `FC`. Annotate the props parameter
instead: `const Foo = ({ bar }: FooProps) => ...`.",
+ selector:
+ "VariableDeclarator[init.type='ArrowFunctionExpression'] >
Identifier > TSTypeAnnotation > TSTypeReference >
Identifier[name=/^(FC|FunctionComponent|VFC)$/]",
+ },
+ {
+ message:
+ 'Import React types by name instead of qualifying them with the
`React` namespace: `import { type ReactElement } from "react"`, then use
`ReactElement`.',
+ selector:
"TSQualifiedName[left.name='React']:not([right.name=/^(FC|FunctionComponent|VFC)$/])",
+ },
+ ],
"perfectionist/sort-objects": [
"error",
{
diff --git
a/providers/edge3/src/airflow/providers/edge3/plugins/www/src/components/StateBadge.tsx
b/providers/edge3/src/airflow/providers/edge3/plugins/www/src/components/StateBadge.tsx
index cbda0810e61..c963ae3f6f4 100644
---
a/providers/edge3/src/airflow/providers/edge3/plugins/www/src/components/StateBadge.tsx
+++
b/providers/edge3/src/airflow/providers/edge3/plugins/www/src/components/StateBadge.tsx
@@ -18,7 +18,7 @@
*/
import { Badge, type BadgeProps } from "@chakra-ui/react";
import type { TaskInstanceState } from "openapi/requests/types.gen";
-import * as React from "react";
+import { forwardRef } from "react";
import { StateIcon } from "./StateIcon";
@@ -26,7 +26,7 @@ export type Props = {
readonly state?: TaskInstanceState | null;
} & BadgeProps;
-export const StateBadge = React.forwardRef<HTMLDivElement, Props>(({ children,
state, ...rest }, ref) => (
+export const StateBadge = forwardRef<HTMLDivElement, Props>(({ children,
state, ...rest }, ref) => (
<Badge
borderRadius="full"
colorPalette={state === null ? "none" : state}
diff --git
a/providers/edge3/src/airflow/providers/edge3/plugins/www/src/components/WorkerStateBadge.tsx
b/providers/edge3/src/airflow/providers/edge3/plugins/www/src/components/WorkerStateBadge.tsx
index 4f6e0deca83..c32cf8a158e 100644
---
a/providers/edge3/src/airflow/providers/edge3/plugins/www/src/components/WorkerStateBadge.tsx
+++
b/providers/edge3/src/airflow/providers/edge3/plugins/www/src/components/WorkerStateBadge.tsx
@@ -18,7 +18,7 @@
*/
import { Badge, type BadgeProps } from "@chakra-ui/react";
import type { EdgeWorkerState } from "openapi/requests/types.gen";
-import * as React from "react";
+import { forwardRef } from "react";
import { WorkerStateIcon } from "./WorkerStateIcon";
@@ -84,7 +84,7 @@ export type Props = {
readonly state?: EdgeWorkerState | null;
} & BadgeProps;
-export const WorkerStateBadge = React.forwardRef<HTMLDivElement, Props>(
+export const WorkerStateBadge = forwardRef<HTMLDivElement, Props>(
({ children, state, ...rest }, ref) => (
<Badge
borderRadius="full"
diff --git
a/providers/edge3/src/airflow/providers/edge3/plugins/www/src/components/WorkerSysinfoBadge.tsx
b/providers/edge3/src/airflow/providers/edge3/plugins/www/src/components/WorkerSysinfoBadge.tsx
index 639c9be7805..450f68ba487 100644
---
a/providers/edge3/src/airflow/providers/edge3/plugins/www/src/components/WorkerSysinfoBadge.tsx
+++
b/providers/edge3/src/airflow/providers/edge3/plugins/www/src/components/WorkerSysinfoBadge.tsx
@@ -17,7 +17,7 @@
* under the License.
*/
import { Badge, type BadgeProps, HoverCard, List, Text } from
"@chakra-ui/react";
-import * as React from "react";
+import { forwardRef } from "react";
import TimeAgo from "react-timeago";
import { LuCheck } from "react-icons/lu";
@@ -97,7 +97,7 @@ export type Props = {
readonly last_heartbeat: string | null | undefined;
} & BadgeProps;
-export const WorkerSysinfoBadge = React.forwardRef<HTMLDivElement, Props>(
+export const WorkerSysinfoBadge = forwardRef<HTMLDivElement, Props>(
({ children, sysinfo, first_online, last_heartbeat, ...rest }, ref) => (
<HoverCard.Root size={"lg"}>
<HoverCard.Trigger>
diff --git
a/providers/edge3/src/airflow/providers/edge3/plugins/www/src/components/ui/Alert.tsx
b/providers/edge3/src/airflow/providers/edge3/plugins/www/src/components/ui/Alert.tsx
index 6eb4c6ee9d7..5d202fd5174 100644
---
a/providers/edge3/src/airflow/providers/edge3/plugins/www/src/components/ui/Alert.tsx
+++
b/providers/edge3/src/airflow/providers/edge3/plugins/www/src/components/ui/Alert.tsx
@@ -18,17 +18,17 @@
*/
/* eslint-disable no-extra-boolean-cast */
import { Alert as ChakraAlert } from "@chakra-ui/react";
-import { forwardRef } from "react";
+import { forwardRef, type ReactNode, type ReactElement } from "react";
import { CloseButton } from "./CloseButton";
export type AlertProps = {
readonly closable?: boolean;
- readonly endElement?: React.ReactNode;
- readonly icon?: React.ReactElement;
+ readonly endElement?: ReactNode;
+ readonly icon?: ReactElement;
readonly onClose?: () => void;
- readonly startElement?: React.ReactNode;
- readonly title?: React.ReactNode;
+ readonly startElement?: ReactNode;
+ readonly title?: ReactNode;
} & Omit<ChakraAlert.RootProps, "title">;
export const Alert = forwardRef<HTMLDivElement, AlertProps>((props, ref) => {
diff --git
a/providers/edge3/src/airflow/providers/edge3/plugins/www/src/components/ui/InputGroup.tsx
b/providers/edge3/src/airflow/providers/edge3/plugins/www/src/components/ui/InputGroup.tsx
index ff8bdf9d662..d1f82f080f9 100644
---
a/providers/edge3/src/airflow/providers/edge3/plugins/www/src/components/ui/InputGroup.tsx
+++
b/providers/edge3/src/airflow/providers/edge3/plugins/www/src/components/ui/InputGroup.tsx
@@ -22,13 +22,13 @@
/* eslint-disable @typescript-eslint/no-unsafe-argument */
import type { BoxProps, InputElementProps } from "@chakra-ui/react";
import { Group, InputElement } from "@chakra-ui/react";
-import { cloneElement, forwardRef } from "react";
+import { cloneElement, forwardRef, type ReactElement, type ReactNode } from
"react";
export type InputGroupProps = {
- readonly children: React.ReactElement;
- readonly endElement?: React.ReactNode;
+ readonly children: ReactElement;
+ readonly endElement?: ReactNode;
readonly endElementProps?: InputElementProps;
- readonly startElement?: React.ReactNode;
+ readonly startElement?: ReactNode;
readonly startElementProps?: InputElementProps;
} & BoxProps;
diff --git
a/providers/edge3/src/airflow/providers/edge3/plugins/www/src/components/ui/Select/Content.tsx
b/providers/edge3/src/airflow/providers/edge3/plugins/www/src/components/ui/Select/Content.tsx
index 4fd441afc1a..96766492d1c 100644
---
a/providers/edge3/src/airflow/providers/edge3/plugins/www/src/components/ui/Select/Content.tsx
+++
b/providers/edge3/src/airflow/providers/edge3/plugins/www/src/components/ui/Select/Content.tsx
@@ -17,11 +17,11 @@
* under the License.
*/
import { Select as ChakraSelect, Portal } from "@chakra-ui/react";
-import { forwardRef } from "react";
+import { forwardRef, type RefObject } from "react";
type ContentProps = {
readonly portalled?: boolean;
- readonly portalRef?: React.RefObject<HTMLElement>;
+ readonly portalRef?: RefObject<HTMLElement>;
} & ChakraSelect.ContentProps;
export const Content = forwardRef<HTMLDivElement, ContentProps>((props, ref)
=> {
diff --git
a/providers/edge3/src/airflow/providers/edge3/plugins/www/src/components/ui/Select/ValueText.tsx
b/providers/edge3/src/airflow/providers/edge3/plugins/www/src/components/ui/Select/ValueText.tsx
index 72b47b89f06..ba57651263c 100644
---
a/providers/edge3/src/airflow/providers/edge3/plugins/www/src/components/ui/Select/ValueText.tsx
+++
b/providers/edge3/src/airflow/providers/edge3/plugins/www/src/components/ui/Select/ValueText.tsx
@@ -18,10 +18,10 @@
*/
import type { CollectionItem } from "@chakra-ui/react";
import { Select as ChakraSelect } from "@chakra-ui/react";
-import { forwardRef } from "react";
+import { forwardRef, type ReactNode } from "react";
type ValueTextProps = {
- readonly children?: (items: Array<CollectionItem>) => React.ReactNode;
+ readonly children?: (items: Array<CollectionItem>) => ReactNode;
} & Omit<ChakraSelect.ValueTextProps, "children">;
export const ValueText = forwardRef<HTMLSpanElement, ValueTextProps>((props,
ref) => {
diff --git
a/providers/edge3/src/airflow/providers/edge3/plugins/www/src/main.tsx
b/providers/edge3/src/airflow/providers/edge3/plugins/www/src/main.tsx
index 60cca732e88..a8090091111 100644
--- a/providers/edge3/src/airflow/providers/edge3/plugins/www/src/main.tsx
+++ b/providers/edge3/src/airflow/providers/edge3/plugins/www/src/main.tsx
@@ -19,7 +19,6 @@
import { ChakraProvider } from "@chakra-ui/react";
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
import { OpenAPI } from "openapi/requests/core/OpenAPI";
-import { FC } from "react";
import { ColorModeProvider } from "src/context/colorMode";
import { EdgeLayout } from "src/layouts/EdgeLayout";
@@ -31,7 +30,7 @@ export type PluginComponentProps = object;
/**
* Main plugin component
*/
-const PluginComponent: FC<PluginComponentProps> = () => {
+const PluginComponent = () => {
// Set the base URL for OpenAPI client from the HTML base tag
const baseHref = document.querySelector("head > base")?.getAttribute("href")
?? "";
const baseUrl = new URL(baseHref, globalThis.location.origin);