fix(frontend): blur upload button so "Upload File" tooltip doesn't persist after file picker closes (#13178)

fix(frontend): blur upload button so tooltip doesn't persist after file picker closes

On the My Files page, clicking the "Upload Files" button kept the
"Upload File" tooltip visible after the OS file picker closed. The
button is wrapped in a Radix tooltip, which shows on focus; when the
file dialog closed, focus was restored to the button and re-triggered
the tooltip. Blur the button on click so focus doesn't return to it.

Also tighten pre-existing `any` types in FilesTab to satisfy the
no-explicit-any lint rule on the staged file.
This commit is contained in:
Eric Hare
2026-05-21 10:02:11 -07:00
committed by GitHub
parent b5db6fd0b2
commit 02e1b402e5
2 changed files with 41 additions and 14 deletions

View File

@ -1,30 +1,48 @@
import { render, screen } from "@testing-library/react";
import React from "react";
import type { ReactNode } from "react";
import { CategoryGroup } from "../categoryGroup";
type MockChildrenProps = {
children?: ReactNode;
className?: string;
};
type MockCategoryDisclosureProps = {
item: { name: string; display_name: string };
openCategories: string[];
};
type MockSearchConfigTriggerProps = {
showConfig: boolean;
setShowConfig: (value: boolean) => void;
};
// Mock the UI components
jest.mock("@/components/ui/sidebar", () => ({
SidebarGroup: ({ children, className }: any) => (
SidebarGroup: ({ children, className }: MockChildrenProps) => (
<div data-testid="sidebar-group" className={className}>
{children}
</div>
),
SidebarGroupContent: ({ children }: any) => (
SidebarGroupContent: ({ children }: MockChildrenProps) => (
<div data-testid="sidebar-group-content">{children}</div>
),
SidebarGroupLabel: ({ children, className }: any) => (
SidebarGroupLabel: ({ children, className }: MockChildrenProps) => (
<div data-testid="sidebar-group-label" className={className}>
{children}
</div>
),
SidebarMenu: ({ children }: any) => (
SidebarMenu: ({ children }: MockChildrenProps) => (
<div data-testid="sidebar-menu">{children}</div>
),
}));
// Mock the CategoryDisclosure component
jest.mock("../categoryDisclouse", () => ({
CategoryDisclosure: ({ item, openCategories }: any) => (
CategoryDisclosure: ({
item,
openCategories,
}: MockCategoryDisclosureProps) => (
<div data-testid={`category-disclosure-${item.name}`}>
CategoryDisclosure for {item.display_name} - Open:{" "}
{openCategories.includes(item.name).toString()}
@ -42,7 +60,10 @@ jest.mock("@/utils/styleUtils", () => ({
// Mock the SearchConfigTrigger component
jest.mock("../searchConfigTrigger", () => ({
SearchConfigTrigger: ({ showConfig, setShowConfig }: any) => (
SearchConfigTrigger: ({
showConfig,
setShowConfig,
}: MockSearchConfigTriggerProps) => (
<button
data-testid="search-config-trigger"
onClick={() => setShowConfig(!showConfig)}

View File

@ -21,6 +21,7 @@ import useUploadFile from "@/hooks/files/use-upload-file";
import DeleteConfirmationModal from "@/modals/deleteConfirmationModal";
import FilesContextMenuComponent from "@/modals/fileManagerModal/components/filesContextMenuComponent";
import useAlertStore from "@/stores/alertStore";
import type { FileType } from "@/types/file_management";
import { formatFileSize } from "@/utils/stringManipulation";
import { FILE_ICONS } from "@/utils/styleUtils";
import { cn } from "@/utils/utils";
@ -30,8 +31,8 @@ import DragWrapComponent from "./dragWrapComponent";
interface FilesTabProps {
quickFilterText: string;
setQuickFilterText: (text: string) => void;
selectedFiles: any[];
setSelectedFiles: (files: any[]) => void;
selectedFiles: FileType[];
setSelectedFiles: (files: FileType[]) => void;
quantitySelected: number;
setQuantitySelected: (quantity: number) => void;
isShiftPressed: boolean;
@ -47,14 +48,14 @@ const FilesTab = ({
isShiftPressed,
}: FilesTabProps) => {
const { t } = useTranslation();
const tableRef = useRef<AgGridReact<any>>(null);
const tableRef = useRef<AgGridReact<FileType>>(null);
const { data: files } = useGetFilesV2();
const setErrorData = useAlertStore((state) => state.setErrorData);
const setSuccessData = useAlertStore((state) => state.setSuccessData);
const { mutate: rename } = usePostRenameFileV2();
const { mutate: deleteFiles, isPending: isDeleting } = useDeleteFilesV2();
const handleRename = (params: NewValueParams<any, any>) => {
const handleRename = (params: NewValueParams<FileType, string>) => {
rename({
id: params.data.id,
name: params.newValue,
@ -85,10 +86,14 @@ const FilesTab = ({
: t("files.uploadedSuccessfully"),
});
}
} catch (error: any) {
} catch (error) {
const message =
error instanceof Error
? error.message
: t("files.errorUploadingDetail");
setErrorData({
title: t("files.errorUploading"),
list: [error.message || t("files.errorUploadingDetail")],
list: [message],
});
}
};
@ -276,7 +281,8 @@ const FilesTab = ({
<ShadTooltip content={t("files.uploadFile")} side="bottom">
<Button
className="!px-3 md:!px-4 md:!pl-3.5"
onClick={async () => {
onClick={async (e) => {
e.currentTarget.blur();
await handleUpload();
}}
id="upload-file-btn"