diff --git a/openmetadata-ui/src/main/resources/ui/src/components/UploadFile/UploadFile.interface.ts b/openmetadata-ui/src/main/resources/ui/src/components/UploadFile/UploadFile.interface.ts
index 811a36b951c..835d27fb077 100644
--- a/openmetadata-ui/src/main/resources/ui/src/components/UploadFile/UploadFile.interface.ts
+++ b/openmetadata-ui/src/main/resources/ui/src/components/UploadFile/UploadFile.interface.ts
@@ -15,6 +15,7 @@ import { RcFile } from 'antd/lib/upload';
declare type BeforeUploadValueType = void | boolean | string | Blob | File;
export interface UploadFileProps {
+ disabled?: boolean;
fileType: string;
beforeUpload?: (
file: RcFile,
diff --git a/openmetadata-ui/src/main/resources/ui/src/components/UploadFile/UploadFile.test.tsx b/openmetadata-ui/src/main/resources/ui/src/components/UploadFile/UploadFile.test.tsx
new file mode 100644
index 00000000000..d2e5117f1a3
--- /dev/null
+++ b/openmetadata-ui/src/main/resources/ui/src/components/UploadFile/UploadFile.test.tsx
@@ -0,0 +1,214 @@
+/*
+ * Copyright 2024 Collate.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import { fireEvent, render, screen, waitFor } from '@testing-library/react';
+import { showErrorToast } from '../../utils/ToastUtils';
+import UploadFile from './UploadFile';
+import { UploadFileProps } from './UploadFile.interface';
+
+jest.mock('../../assets/svg/ic-drag-drop.svg', () => ({
+ ReactComponent: () =>
ImportIcon
,
+}));
+
+jest.mock('../common/Loader/Loader', () => {
+ return jest.fn(() => Loading...
);
+});
+
+jest.mock('../../utils/ToastUtils', () => ({
+ showErrorToast: jest.fn(),
+}));
+
+jest.mock('../../utils/CommonUtils', () => ({
+ Transi18next: jest
+ .fn()
+ .mockReturnValue('message.drag-and-drop-or-browse-csv-files-here'),
+}));
+
+describe('UploadFile Component', () => {
+ const defaultProps: UploadFileProps = {
+ fileType: '.csv',
+ onCSVUploaded: jest.fn(),
+ };
+
+ afterEach(() => {
+ jest.clearAllMocks();
+ });
+
+ it('should render the upload component with correct props', () => {
+ render();
+
+ expect(screen.getByTestId('upload-file-widget')).toBeInTheDocument();
+ expect(screen.getByTestId('import-icon')).toBeInTheDocument();
+ expect(
+ screen.getByText('message.drag-and-drop-or-browse-csv-files-here')
+ ).toBeInTheDocument();
+ });
+
+ it('should render with disabled state', () => {
+ render();
+
+ const uploadWidget = screen.getByTestId('upload-file-widget');
+
+ expect(uploadWidget).toHaveAttribute('disabled');
+ });
+
+ it('should not render with disabled state', () => {
+ render();
+
+ const uploadWidget = screen.getByTestId('upload-file-widget');
+
+ expect(uploadWidget).not.toHaveAttribute('disabled');
+ });
+
+ it('should render with custom file type', () => {
+ render();
+
+ const uploadWidget = screen.getByTestId('upload-file-widget');
+
+ expect(uploadWidget).toHaveAttribute('accept', '.json,.xml');
+ });
+
+ it('should call onCSVUploaded when file is uploaded successfully', async () => {
+ const mockOnCSVUploaded = jest.fn();
+
+ render();
+
+ const uploadWidget = screen.getByTestId('upload-file-widget');
+ const file = new File(['test,csv,content'], 'test.csv', {
+ type: 'text/csv',
+ });
+
+ fireEvent.drop(uploadWidget, {
+ dataTransfer: {
+ files: [file],
+ },
+ });
+
+ await waitFor(() => {
+ expect(mockOnCSVUploaded).toHaveBeenCalled();
+ });
+ });
+
+ it('should handle file upload error', async () => {
+ const mockOnCSVUploaded = jest.fn();
+
+ render();
+
+ const uploadWidget = screen.getByTestId('upload-file-widget');
+
+ // Create a file that will cause an error when read
+ const file = new File(['test content'], 'test.csv', {
+ type: 'text/csv',
+ });
+
+ // Mock FileReader to throw an error
+ const originalFileReader = global.FileReader;
+ global.FileReader = jest.fn().mockImplementation(() => ({
+ readAsText: jest.fn(() => {
+ throw new Error('File read error');
+ }),
+ onerror: null,
+ })) as any;
+
+ fireEvent.drop(uploadWidget, {
+ dataTransfer: {
+ files: [file],
+ },
+ });
+
+ await waitFor(() => {
+ expect(showErrorToast).toHaveBeenCalled();
+ });
+
+ // Restore original FileReader
+ global.FileReader = originalFileReader;
+ });
+
+ it('should call beforeUpload when provided', () => {
+ const mockBeforeUpload = jest.fn(() => true);
+ render();
+
+ const uploadWidget = screen.getByTestId('upload-file-widget');
+ const file = new File(['test content'], 'test.csv', { type: 'text/csv' });
+
+ fireEvent.drop(uploadWidget, {
+ dataTransfer: {
+ files: [file],
+ },
+ });
+
+ expect(mockBeforeUpload).toHaveBeenCalledWith(
+ expect.any(Object),
+ expect.any(Array)
+ );
+ });
+
+ it('should not upload file when beforeUpload returns false', () => {
+ const mockBeforeUpload = jest.fn(() => false);
+ const mockOnCSVUploaded = jest.fn();
+
+ render(
+
+ );
+
+ const uploadWidget = screen.getByTestId('upload-file-widget');
+ const file = new File(['test content'], 'test.csv', { type: 'text/csv' });
+
+ fireEvent.drop(uploadWidget, {
+ dataTransfer: {
+ files: [file],
+ },
+ });
+
+ expect(mockBeforeUpload).toHaveBeenCalled();
+ expect(mockOnCSVUploaded).not.toHaveBeenCalled();
+ });
+
+ it('should handle async beforeUpload function', async () => {
+ const mockBeforeUpload = jest.fn().mockResolvedValue(true);
+ const mockOnCSVUploaded = jest.fn();
+
+ render(
+
+ );
+
+ const uploadWidget = screen.getByTestId('upload-file-widget');
+ const file = new File(['test content'], 'test.csv', { type: 'text/csv' });
+
+ fireEvent.drop(uploadWidget, {
+ dataTransfer: {
+ files: [file],
+ },
+ });
+
+ await waitFor(() => {
+ expect(mockBeforeUpload).toHaveBeenCalled();
+ });
+ });
+
+ it('should render browse text correctly', () => {
+ render();
+
+ expect(
+ screen.getByText('message.drag-and-drop-or-browse-csv-files-here')
+ ).toBeInTheDocument();
+ });
+});
diff --git a/openmetadata-ui/src/main/resources/ui/src/components/UploadFile/UploadFile.tsx b/openmetadata-ui/src/main/resources/ui/src/components/UploadFile/UploadFile.tsx
index 69d10408e01..1d675a554d0 100644
--- a/openmetadata-ui/src/main/resources/ui/src/components/UploadFile/UploadFile.tsx
+++ b/openmetadata-ui/src/main/resources/ui/src/components/UploadFile/UploadFile.tsx
@@ -20,9 +20,11 @@ import { ReactComponent as ImportIcon } from '../../assets/svg/ic-drag-drop.svg'
import { Transi18next } from '../../utils/CommonUtils';
import { showErrorToast } from '../../utils/ToastUtils';
import Loader from '../common/Loader/Loader';
+import './upload-file.less';
import { UploadFileProps } from './UploadFile.interface';
const UploadFile: FC = ({
+ disabled,
fileType,
beforeUpload,
onCSVUploaded,
@@ -55,9 +57,10 @@ const UploadFile: FC = ({
= ({
direction="vertical"
size={42}>
-
+
}
+ renderElement={}
values={{
text: t('label.browse'),
}}
diff --git a/openmetadata-ui/src/main/resources/ui/src/components/UploadFile/upload-file.less b/openmetadata-ui/src/main/resources/ui/src/components/UploadFile/upload-file.less
new file mode 100644
index 00000000000..4231ee9608f
--- /dev/null
+++ b/openmetadata-ui/src/main/resources/ui/src/components/UploadFile/upload-file.less
@@ -0,0 +1,35 @@
+/*
+ * Copyright 2025 Collate.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+@import (reference) '../../styles/variables.less';
+
+.file-dragger-wrapper {
+ padding: @padding-lg;
+ background: @white;
+ .ant-typography {
+ font-weight: 500;
+ font-size: 16px;
+
+ .browse-text {
+ color: @primary-color;
+ }
+ }
+ .ant-upload-disabled {
+ .ant-typography {
+ color: @grey-300;
+
+ .browse-text {
+ color: @grey-300;
+ }
+ }
+ }
+}
diff --git a/openmetadata-ui/src/main/resources/ui/src/pages/EntityImport/BulkEntityImportPage/BulkEntityImportPage.tsx b/openmetadata-ui/src/main/resources/ui/src/pages/EntityImport/BulkEntityImportPage/BulkEntityImportPage.tsx
index a892e3834b4..eba168a7b65 100644
--- a/openmetadata-ui/src/main/resources/ui/src/pages/EntityImport/BulkEntityImportPage/BulkEntityImportPage.tsx
+++ b/openmetadata-ui/src/main/resources/ui/src/pages/EntityImport/BulkEntityImportPage/BulkEntityImportPage.tsx
@@ -552,6 +552,10 @@ const BulkEntityImportPage = () => {
) : (