You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@airflow.apache.org by ry...@apache.org on 2025/10/30 14:11:22 UTC
(airflow) 02/02: Add settings to auto-apply linting, fix linting errors (#57510)
This is an automated email from the ASF dual-hosted git repository.
ryanahamilton pushed a commit to branch backport-nav-updates-to-v3-1-test
in repository https://gitbox.apache.org/repos/asf/airflow.git
commit 577fbed7f9b50d8c97f164bad1c052b8d0634286
Author: Ryan Hamilton <ry...@ryanahamilton.com>
AuthorDate: Thu Oct 30 10:02:15 2025 -0400
Add settings to auto-apply linting, fix linting errors (#57510)
---
.gitignore | 2 -
.vscode/extensions.json | 5 +
.vscode/settings.json | 38 +++++++
.../src/airflow/ui/src/layouts/Nav/Nav.tsx | 6 +-
.../src/airflow/ui/src/layouts/Nav/NavButton.tsx | 112 +++++++++++----------
.../airflow/ui/src/layouts/Nav/PluginMenuItem.tsx | 19 ++--
.../src/airflow/ui/src/layouts/Nav/PluginMenus.tsx | 4 +-
.../ui/src/layouts/Nav/UserSettingsButton.tsx | 4 +-
8 files changed, 121 insertions(+), 69 deletions(-)
diff --git a/.gitignore b/.gitignore
index e7fb2a5403d..1528de626bd 100644
--- a/.gitignore
+++ b/.gitignore
@@ -171,8 +171,6 @@ npm-debug.log*
yarn-debug.log*
yarn-error.log*
pnpm-debug.log*
-.vscode/*
-!.vscode/extensions.json
/.vite/
# Exclude the ui .vite dir
airflow-core/src/airflow/ui/.vite/
diff --git a/.vscode/extensions.json b/.vscode/extensions.json
new file mode 100644
index 00000000000..0e6d5fd64e4
--- /dev/null
+++ b/.vscode/extensions.json
@@ -0,0 +1,5 @@
+{
+ "recommendations": [
+ "esbenp.prettier-vscode",
+ ]
+}
diff --git a/.vscode/settings.json b/.vscode/settings.json
new file mode 100644
index 00000000000..aeae611e1fe
--- /dev/null
+++ b/.vscode/settings.json
@@ -0,0 +1,38 @@
+{
+ "[javascript]": {
+ "editor.defaultFormatter": "esbenp.prettier-vscode",
+ "editor.formatOnSave": true,
+ "editor.codeActionsOnSave": {
+ "source.fixAll.eslint": "explicit"
+ }
+ },
+ "[javascriptreact]": {
+ "editor.defaultFormatter": "esbenp.prettier-vscode",
+ "editor.formatOnSave": true,
+ "editor.codeActionsOnSave": {
+ "source.fixAll.eslint": "explicit"
+ }
+ },
+ "[typescript]": {
+ "editor.defaultFormatter": "esbenp.prettier-vscode",
+ "editor.formatOnSave": true,
+ "editor.codeActionsOnSave": {
+ "source.fixAll.eslint": "explicit"
+ }
+ },
+ "[typescriptreact]": {
+ "editor.defaultFormatter": "esbenp.prettier-vscode",
+ "editor.formatOnSave": true,
+ "editor.codeActionsOnSave": {
+ "source.fixAll.eslint": "explicit"
+ }
+ },
+ "[json]": {
+ "editor.defaultFormatter": "esbenp.prettier-vscode",
+ "editor.formatOnSave": true
+ },
+ "[jsonc]": {
+ "editor.defaultFormatter": "esbenp.prettier-vscode",
+ "editor.formatOnSave": true
+ },
+}
diff --git a/airflow-core/src/airflow/ui/src/layouts/Nav/Nav.tsx b/airflow-core/src/airflow/ui/src/layouts/Nav/Nav.tsx
index 5fa459b7959..2203df73880 100644
--- a/airflow-core/src/airflow/ui/src/layouts/Nav/Nav.tsx
+++ b/airflow-core/src/airflow/ui/src/layouts/Nav/Nav.tsx
@@ -145,9 +145,9 @@ export const Nav = () => {
width={16}
zIndex="docked"
>
- <Flex alignItems="center" flexDir="column" width="100%" gap={1}>
- <Box asChild boxSize={14} display="flex" alignItems="center" justifyContent="center">
- <Link to="/" title={translate("nav.home")}>
+ <Flex alignItems="center" flexDir="column" gap={1} width="100%">
+ <Box alignItems="center" asChild boxSize={14} display="flex" justifyContent="center">
+ <Link title={translate("nav.home")} to="/">
<AirflowPin
_motionSafe={{
_hover: {
diff --git a/airflow-core/src/airflow/ui/src/layouts/Nav/NavButton.tsx b/airflow-core/src/airflow/ui/src/layouts/Nav/NavButton.tsx
index 3ebea71bef1..eaa13f57f1b 100644
--- a/airflow-core/src/airflow/ui/src/layouts/Nav/NavButton.tsx
+++ b/airflow-core/src/airflow/ui/src/layouts/Nav/NavButton.tsx
@@ -17,8 +17,8 @@
* under the License.
*/
import { Box, type BoxProps, Button, Icon, type IconProps, Link, type ButtonProps } from "@chakra-ui/react";
-import { useMemo, type ForwardRefExoticComponent, type RefAttributes } from "react";
-import { IconType } from "react-icons";
+import { type ReactNode, useMemo, type ForwardRefExoticComponent, type RefAttributes } from "react";
+import type { IconType } from "react-icons";
import { Link as RouterLink, useMatch } from "react-router-dom";
const commonLabelProps: BoxProps = {
@@ -31,63 +31,73 @@ const commonLabelProps: BoxProps = {
};
type NavButtonProps = {
- readonly icon: IconType | ForwardRefExoticComponent<IconProps & RefAttributes<SVGSVGElement>>;
+ readonly icon: ForwardRefExoticComponent<IconProps & RefAttributes<SVGSVGElement>> | IconType;
readonly isExternal?: boolean;
+ readonly pluginIcon?: ReactNode;
readonly title: string;
readonly to?: string;
} & ButtonProps;
-export const NavButton = ({ icon, isExternal = false, title, to, ...rest }: NavButtonProps) => {
+export const NavButton = ({ icon, isExternal = false, pluginIcon, title, to, ...rest }: NavButtonProps) => {
// Use useMatch to determine if the current route matches the button's destination
// This provides the same functionality as NavLink's isActive prop
// Only applies to buttons with a to prop (but needs to be before any return statements)
- const match = to ? useMatch({
- path: to,
- end: to === "/" // Only exact match for root path
- }) : undefined;
+ const match = useMatch({
+ end: to === "/", // Only exact match for root path
+ path: to ?? "",
+ });
// Only applies to buttons with a to prop
- const isActive = Boolean(match);
+ const isActive = Boolean(to) ? Boolean(match) : false;
- const commonButtonProps = useMemo<ButtonProps>(() => ({
- _expanded: isActive ? undefined : {
- bg: "brand.emphasized", // Even darker for better light mode contrast
- color: "fg",
- },
- _focus: isActive ? undefined : {
- color: "fg",
- },
- _hover: isActive ? undefined : {
- bg: "brand.emphasized", // Even darker for better light mode contrast
- color: "fg",
- _active: {
- bg: "brand.solid",
- color: "white",
- },
- },
- alignItems: "center",
- bg: isActive ? "brand.solid" : undefined,
- borderRadius: "md",
- borderWidth: 0,
- boxSize: 14,
- color: isActive ? "white" : "fg.muted",
- colorPalette: "brand",
- cursor: "pointer",
- flexDir: "column",
- gap: 0,
- overflow: "hidden",
- padding: 0,
- textDecoration: "none",
- title,
- transition: "background-color 0.2s ease, color 0.2s ease",
- variant: "plain",
- whiteSpace: "wrap",
- ...rest,
- }), [isActive, rest, title]);
+ const commonButtonProps = useMemo<ButtonProps>(
+ () => ({
+ _expanded: isActive
+ ? undefined
+ : {
+ bg: "brand.emphasized", // Even darker for better light mode contrast
+ color: "fg",
+ },
+ _focus: isActive
+ ? undefined
+ : {
+ color: "fg",
+ },
+ _hover: isActive
+ ? undefined
+ : {
+ _active: {
+ bg: "brand.solid",
+ color: "white",
+ },
+ bg: "brand.emphasized", // Even darker for better light mode contrast
+ color: "fg",
+ },
+ alignItems: "center",
+ bg: isActive ? "brand.solid" : undefined,
+ borderRadius: "md",
+ borderWidth: 0,
+ boxSize: 14,
+ color: isActive ? "white" : "fg.muted",
+ colorPalette: "brand",
+ cursor: "pointer",
+ flexDir: "column",
+ gap: 0,
+ overflow: "hidden",
+ padding: 0,
+ textDecoration: "none",
+ title,
+ transition: "background-color 0.2s ease, color 0.2s ease",
+ variant: "plain",
+ whiteSpace: "wrap",
+ ...rest,
+ }),
+ [isActive, rest, title],
+ );
if (to === undefined) {
return (
<Button {...commonButtonProps}>
- <Icon as={icon} boxSize={5} />
+ {pluginIcon ?? <Icon as={icon} boxSize={5} />}
<Box {...commonLabelProps}>{title}</Box>
</Button>
);
@@ -95,9 +105,9 @@ export const NavButton = ({ icon, isExternal = false, title, to, ...rest }: NavB
if (isExternal) {
return (
- <Link href={to} asChild rel="noopener noreferrer" target="_blank">
+ <Link asChild href={to} rel="noopener noreferrer" target="_blank">
<Button {...commonButtonProps}>
- <Icon as={icon} boxSize={5} />
+ {pluginIcon ?? <Icon as={icon} boxSize={5} />}
<Box {...commonLabelProps}>{title}</Box>
</Button>
</Link>
@@ -105,13 +115,9 @@ export const NavButton = ({ icon, isExternal = false, title, to, ...rest }: NavB
}
return (
- <Button
- as={Link}
- asChild
- {...commonButtonProps}
- >
+ <Button as={Link} asChild {...commonButtonProps}>
<RouterLink to={to}>
- <Icon as={icon} boxSize={5} />
+ {pluginIcon ?? <Icon as={icon} boxSize={5} />}
<Box {...commonLabelProps}>{title}</Box>
</RouterLink>
</Button>
diff --git a/airflow-core/src/airflow/ui/src/layouts/Nav/PluginMenuItem.tsx b/airflow-core/src/airflow/ui/src/layouts/Nav/PluginMenuItem.tsx
index c7372703237..5d36cf59e10 100644
--- a/airflow-core/src/airflow/ui/src/layouts/Nav/PluginMenuItem.tsx
+++ b/airflow-core/src/airflow/ui/src/layouts/Nav/PluginMenuItem.tsx
@@ -16,7 +16,7 @@
* specific language governing permissions and limitations
* under the License.
*/
-import { Link, Image, Menu } from "@chakra-ui/react";
+import { Link, Image, Menu, Icon, Box } from "@chakra-ui/react";
import { FiExternalLink } from "react-icons/fi";
import { LuPlug } from "react-icons/lu";
import { RiArchiveStackLine } from "react-icons/ri";
@@ -46,11 +46,11 @@ export const PluginMenuItem = ({
const displayIcon = colorMode === "dark" && typeof iconDarkMode === "string" ? iconDarkMode : icon;
const pluginIcon =
typeof displayIcon === "string" ? (
- <Image height="20px" mr={topLevel ? 0 : 2} src={displayIcon} width="20px" />
+ <Image boxSize={5} src={displayIcon} />
) : urlRoute === "legacy-fab-views" ? (
- <RiArchiveStackLine size="20px" style={{ marginRight: topLevel ? 0 : "8px" }} />
+ <Icon as={RiArchiveStackLine} boxSize={5} />
) : (
- <LuPlug size="20px" style={{ marginRight: topLevel ? 0 : "8px" }} />
+ <Icon as={LuPlug} boxSize={5} />
);
const isExternal = urlRoute === undefined || urlRoute === null;
@@ -58,9 +58,10 @@ export const PluginMenuItem = ({
if (topLevel) {
return (
<NavButton
- icon={pluginIcon}
+ icon={LuPlug}
isExternal={isExternal}
key={name}
+ pluginIcon={pluginIcon}
title={name}
to={isExternal ? href : `plugin/${urlRoute}`}
/>
@@ -80,13 +81,15 @@ export const PluginMenuItem = ({
width="100%"
>
{pluginIcon}
- {name}
- <FiExternalLink />
+ <Box flex="1">{name}</Box>
+ <Icon as={FiExternalLink} boxSize={4} color="fg.muted" />
</Link>
) : (
<RouterLink style={{ outline: "none" }} to={`plugin/${urlRoute}`}>
{pluginIcon}
- {name}
+ <Box flex="1" ml={2}>
+ {name}
+ </Box>
</RouterLink>
)}
</Menu.Item>
diff --git a/airflow-core/src/airflow/ui/src/layouts/Nav/PluginMenus.tsx b/airflow-core/src/airflow/ui/src/layouts/Nav/PluginMenus.tsx
index c8727804890..9ac31f6947f 100644
--- a/airflow-core/src/airflow/ui/src/layouts/Nav/PluginMenus.tsx
+++ b/airflow-core/src/airflow/ui/src/layouts/Nav/PluginMenus.tsx
@@ -16,7 +16,7 @@
* specific language governing permissions and limitations
* under the License.
*/
-import { Box } from "@chakra-ui/react";
+import { Box, Icon } from "@chakra-ui/react";
import { useTranslation } from "react-i18next";
import { FiChevronRight } from "react-icons/fi";
import { LuPlug } from "react-icons/lu";
@@ -63,7 +63,7 @@ export const PluginMenus = ({ navItems }: { readonly navItems: Array<NavItemResp
<Menu.Root key={key} positioning={{ placement: "right" }}>
<Menu.TriggerItem display="flex" justifyContent="space-between">
{key}
- <FiChevronRight />
+ <Icon as={FiChevronRight} boxSize={4} color="fg.muted" />
</Menu.TriggerItem>
<Menu.Content>
{menuButtons.map((navItem) => (
diff --git a/airflow-core/src/airflow/ui/src/layouts/Nav/UserSettingsButton.tsx b/airflow-core/src/airflow/ui/src/layouts/Nav/UserSettingsButton.tsx
index 15ee91b0422..84d8c385fed 100644
--- a/airflow-core/src/airflow/ui/src/layouts/Nav/UserSettingsButton.tsx
+++ b/airflow-core/src/airflow/ui/src/layouts/Nav/UserSettingsButton.tsx
@@ -121,7 +121,9 @@ export const UserSettingsButton = ({ externalViews }: { readonly externalViews:
value={dagView}
>
<Icon as={dagView === "grid" ? MdOutlineAccountTree : FiGrid} boxSize={4} />
- <Box flex="1">{dagView === "grid" ? translate("defaultToGraphView") : translate("defaultToGridView")}</Box>
+ <Box flex="1">
+ {dagView === "grid" ? translate("defaultToGraphView") : translate("defaultToGridView")}
+ </Box>
</Menu.Item>
<TimezoneMenuItem onOpen={onOpenTimezone} />
{externalViews.map((view) => (