From 4dd05f6c522aa6bb49a46463ba4911e833d6bfff Mon Sep 17 00:00:00 2001 From: Ram Prasad Agarwal Date: Mon, 13 Oct 2025 11:15:38 +0530 Subject: [PATCH 1/6] [ui-impoter] fix: settings tab for max width in input fields --- .../js/apps/newimporter/SettingsTab/SettingsTab.scss | 9 +++++++++ .../js/apps/newimporter/SettingsTab/SettingsTabConfig.ts | 5 +---- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/desktop/core/src/desktop/js/apps/newimporter/SettingsTab/SettingsTab.scss b/desktop/core/src/desktop/js/apps/newimporter/SettingsTab/SettingsTab.scss index d0eb0800b9d..5f8d5ee92f2 100644 --- a/desktop/core/src/desktop/js/apps/newimporter/SettingsTab/SettingsTab.scss +++ b/desktop/core/src/desktop/js/apps/newimporter/SettingsTab/SettingsTab.scss @@ -28,6 +28,11 @@ display: flex; flex-direction: column; gap: vars.$cdl-spacing-m; + max-width: 1024px; + + @media (width <= 1024px) { + width: 100%; + } } &__section { @@ -47,6 +52,10 @@ flex: 1; width: 100%; gap: vars.$cdl-spacing-m; + + @media (width <= 640px) { + flex-direction: column; + } } &__fields { diff --git a/desktop/core/src/desktop/js/apps/newimporter/SettingsTab/SettingsTabConfig.ts b/desktop/core/src/desktop/js/apps/newimporter/SettingsTab/SettingsTabConfig.ts index 3c3cf3b15a4..e6acba592c1 100644 --- a/desktop/core/src/desktop/js/apps/newimporter/SettingsTab/SettingsTabConfig.ts +++ b/desktop/core/src/desktop/js/apps/newimporter/SettingsTab/SettingsTabConfig.ts @@ -51,7 +51,7 @@ export const ADVANCED_SETTINGS_CONFIG: Record = { description: [ { name: 'description', - type: FieldType.INPUT, + type: FieldType.TEXTAREA, label: 'Description', placeholder: "A table to store customer data imported from the marketing team's CRM.", tooltip: @@ -139,7 +139,6 @@ export const ADVANCED_SETTINGS_CONFIG: Record = { label: 'Field', placeholder: 'Choose an option', options: DELIMITER_OPTIONS, - tooltip: 'Field delimiter', isHidden: (context: SettingsContext) => !context.customCharDelimiters }, { @@ -148,7 +147,6 @@ export const ADVANCED_SETTINGS_CONFIG: Record = { label: 'Array Map', placeholder: 'Choose an option', options: DELIMITER_OPTIONS, - tooltip: 'Array map delimiter', isHidden: (context: SettingsContext) => !context.customCharDelimiters }, { @@ -157,7 +155,6 @@ export const ADVANCED_SETTINGS_CONFIG: Record = { label: 'Struct', placeholder: 'Choose an option', options: DELIMITER_OPTIONS, - tooltip: 'Struct delimiter', isHidden: (context: SettingsContext) => !context.customCharDelimiters } ] From a4f3386370e7260bf70b24168bdf28286bc69ece Mon Sep 17 00:00:00 2001 From: Ram Prasad Agarwal Date: Wed, 15 Oct 2025 23:29:26 +0530 Subject: [PATCH 2/6] feat: add file chooser input field to form input component --- .../SettingsTab/SettingsTabConfig.ts | 2 +- .../FileChooserModal/FileChooserModal.tsx | 203 +++++++-------- .../FileChooserInput/FileChooserInput.scss | 39 +++ .../FileChooserInput.test.tsx | 242 ++++++++++++++++++ .../FileChooserInput/FileChooserInput.tsx | 82 ++++++ .../reactComponents/FormInput/FormInput.tsx | 15 +- 6 files changed, 473 insertions(+), 110 deletions(-) create mode 100644 desktop/core/src/desktop/js/reactComponents/FormInput/FileChooserInput/FileChooserInput.scss create mode 100644 desktop/core/src/desktop/js/reactComponents/FormInput/FileChooserInput/FileChooserInput.test.tsx create mode 100644 desktop/core/src/desktop/js/reactComponents/FormInput/FileChooserInput/FileChooserInput.tsx diff --git a/desktop/core/src/desktop/js/apps/newimporter/SettingsTab/SettingsTabConfig.ts b/desktop/core/src/desktop/js/apps/newimporter/SettingsTab/SettingsTabConfig.ts index e6acba592c1..36dc25eab4e 100644 --- a/desktop/core/src/desktop/js/apps/newimporter/SettingsTab/SettingsTabConfig.ts +++ b/desktop/core/src/desktop/js/apps/newimporter/SettingsTab/SettingsTabConfig.ts @@ -118,7 +118,7 @@ export const ADVANCED_SETTINGS_CONFIG: Record = { }, { name: 'externalLocation', - type: FieldType.INPUT, + type: FieldType.FILECHOOSER, placeholder: 'External location', isHidden: (context: SettingsContext) => !context.useExternalLocation } diff --git a/desktop/core/src/desktop/js/apps/storageBrowser/FileChooserModal/FileChooserModal.tsx b/desktop/core/src/desktop/js/apps/storageBrowser/FileChooserModal/FileChooserModal.tsx index de8e88e81b1..c03295f974d 100644 --- a/desktop/core/src/desktop/js/apps/storageBrowser/FileChooserModal/FileChooserModal.tsx +++ b/desktop/core/src/desktop/js/apps/storageBrowser/FileChooserModal/FileChooserModal.tsx @@ -121,32 +121,25 @@ const FileChooserModal = ({ [setSearchTerm] ); - const getColumns = (file: FileChooserTableData) => { - const columns: ColumnProps[] = []; - for (const key of Object.keys(file)) { - const column: ColumnProps = { - dataIndex: key, - key: `${key}` - }; - if (key === 'name') { - column.render = (_, record: FileChooserTableData) => ( - - - {record.type === BrowserViewType.dir ? : } - - - {record.name} - - - ); - } - columns.push(column); + const columnsConfig: ColumnProps[] = [ + { + dataIndex: 'name', + key: 'name', + render: (_, record: FileChooserTableData) => ( + + + {record.type === BrowserViewType.dir ? : } + + + {record.name} + + + ) } - return columns.filter(col => col.dataIndex !== 'type' && col.dataIndex !== 'path'); - }; + ]; const onRowClicked = (record: FileChooserTableData) => { return { @@ -243,96 +236,90 @@ const FileChooserModal = ({ } ]; - const TableContent = ( - - - loading={loading && !polling} - data={tableData} - isDynamicHeight - rowKey={r => `${r.path}__${r.type}__${r.name}`} - locale={locale} - columns={getColumns(tableData[0] ?? {})} - rowClassName={record => - record.type === BrowserViewType.file && !isFileSelectionAllowed - ? classNames('hue-filechooser-modal__table-row', 'disabled-row') - : 'hue-filechooser-modal__table-row' - } - onRowClick={onRowClicked} - showHeader={false} - /> - - ); + if (!showModal) { + return <>; + } return ( - <> - {showModal && ( - setShowCreateFolderModal(true)} - cancelText={cancelText} - onCancel={onClose} + setShowCreateFolderModal(true)} + cancelText={cancelText} + onCancel={onClose} + > +
+
+ +
+ { + handleSearch(event.target.value); + }} + /> + + + + loading={loading && !polling} + data={tableData} + isDynamicHeight + rowKey={r => `${r.path}__${r.type}__${r.name}`} + locale={locale} + columns={columnsConfig} + rowClassName={record => + record.type === BrowserViewType.file && !isFileSelectionAllowed + ? classNames('hue-filechooser-modal__table-row', 'disabled-row') + : 'hue-filechooser-modal__table-row' + } + onRowClick={onRowClicked} + showHeader={false} + /> + + +
+ + {showCreateFolderModal && ( + { + setShowCreateFolderModal(false); + setCreateFolderValue(''); + }} + onPrimaryClick={handleCreate} > -
-
- -
+ {/* TODO: Refactor CreateAndUpload to reuse */} + { - handleSearch(event.target.value); + defaultValue={createFolderValue} + disabled={createFolderLoading} + onPressEnter={() => handleCreate()} + ref={createFolderInputRef} + onChange={e => { + setCreateFolderValue(e.target.value); }} /> - {isUploadEnabled ? ( - {TableContent} - ) : ( - TableContent - )} -
- - {showCreateFolderModal && ( - { - setShowCreateFolderModal(false); - setCreateFolderValue(''); - }} - onPrimaryClick={handleCreate} - > - {/* TODO: Refactor CreateAndUpload to reuse */} - - handleCreate()} - ref={createFolderInputRef} - onChange={e => { - setCreateFolderValue(e.target.value); - }} - /> - - - )} -
+ + )} - +
); }; diff --git a/desktop/core/src/desktop/js/reactComponents/FormInput/FileChooserInput/FileChooserInput.scss b/desktop/core/src/desktop/js/reactComponents/FormInput/FileChooserInput/FileChooserInput.scss new file mode 100644 index 00000000000..b05f9ae6912 --- /dev/null +++ b/desktop/core/src/desktop/js/reactComponents/FormInput/FileChooserInput/FileChooserInput.scss @@ -0,0 +1,39 @@ +// Licensed to Cloudera, Inc. under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. Cloudera, Inc. licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +@use '../../../components/styles/variables' as vars; + +.antd.cuix { + .hue-form-input__file-chooser { + display: flex; + align-items: stretch; + position: relative; + + .cuix.input { + flex: 1; + border-top-right-radius: 0; + border-bottom-right-radius: 0; + } + + &__button { + white-space: nowrap; + flex-shrink: 0; + border-top-left-radius: 0; + border-bottom-left-radius: 0; + margin-left: -1px; + } + } +} diff --git a/desktop/core/src/desktop/js/reactComponents/FormInput/FileChooserInput/FileChooserInput.test.tsx b/desktop/core/src/desktop/js/reactComponents/FormInput/FileChooserInput/FileChooserInput.test.tsx new file mode 100644 index 00000000000..31b71c07f94 --- /dev/null +++ b/desktop/core/src/desktop/js/reactComponents/FormInput/FileChooserInput/FileChooserInput.test.tsx @@ -0,0 +1,242 @@ +// Licensed to Cloudera, Inc. under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. Cloudera, Inc. licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +import React from 'react'; +import { render, screen, waitFor } from '@testing-library/react'; +import userEvent from '@testing-library/user-event'; +import '@testing-library/jest-dom'; +import FileChooserInput from './FileChooserInput'; + +jest.mock('../../../utils/hooks/useLoadData/useLoadData', () => ({ + __esModule: true, + default: jest.fn() +})); + +import useLoadData from '../../../utils/hooks/useLoadData/useLoadData'; + +const mockUseLoadData = useLoadData as jest.MockedFunction; + +describe('FileChooserInput', () => { + const mockOnChange = jest.fn(); + + const mockDirectoryData = { + files: [ + { + path: '/test/directory/file1.txt', + type: 'file', + size: 1024, + mtime: Date.now(), + user: 'testuser', + group: 'testgroup', + rwx: 'rw-r--r--', + mode: 644, + atime: Date.now(), + blockSize: 4096, + replication: 3 + }, + { + path: '/test/directory/file2.txt', + type: 'file', + size: 2048, + mtime: Date.now(), + user: 'testuser', + group: 'testgroup', + rwx: 'rw-r--r--', + mode: 644, + atime: Date.now(), + blockSize: 4096, + replication: 3 + }, + { + path: '/test/directory/subfolder', + type: 'dir', + size: 0, + mtime: Date.now(), + user: 'testuser', + group: 'testgroup', + rwx: 'rwxr-xr-x', + mode: 755, + atime: Date.now(), + blockSize: 0, + replication: 0 + } + ], + page: { + number: 1, + numPages: 1, + startIndex: 0, + endIndex: 3, + totalRecords: 3, + pageSize: 1000, + previousPage: 1, + nextPage: 1 + } + }; + + beforeEach(() => { + jest.clearAllMocks(); + mockUseLoadData.mockReturnValue({ + data: mockDirectoryData, + loading: false, + error: undefined, + reloadData: jest.fn() + }); + }); + + it('should render input field with value', () => { + render(); + + const input = screen.getByRole('textbox'); + expect(input).toBeInTheDocument(); + expect(input).toHaveValue('/test/path'); + }); + + it('should render Choose button', () => { + render(); + + const button = screen.getByRole('button', { name: 'Choose' }); + expect(button).toBeInTheDocument(); + expect(button).toHaveTextContent('Choose'); + }); + + it('should render with placeholder', () => { + render(); + + const input = screen.getByRole('textbox'); + expect(input).toHaveAttribute('placeholder', 'Enter file path'); + }); + + it('should call onChange when typing in input field', async () => { + const user = userEvent.setup(); + render(); + + const input = screen.getByRole('textbox'); + await user.type(input, '/new/path'); + + expect(mockOnChange).toHaveBeenCalled(); + }); + + it('should show error status when error prop is true', () => { + render(); + + const input = screen.getByRole('textbox'); + expect(input).toHaveClass('ant-input-status-error'); + }); + + it('should open FileChooserModal when Choose button is clicked', async () => { + const user = userEvent.setup(); + render(); + + const button = screen.getByRole('button', { name: 'Choose' }); + await user.click(button); + + await waitFor(() => { + expect(screen.getByText('Choose a file')).toBeInTheDocument(); + }); + }); + + it('should update value and close modal when file is selected', async () => { + const user = userEvent.setup(); + render(); + + const button = screen.getByRole('button', { name: 'Choose' }); + await user.click(button); + + await waitFor(() => { + expect(screen.getByText('Choose a file')).toBeInTheDocument(); + }); + + await waitFor(() => { + expect(screen.getByText('file1.txt')).toBeInTheDocument(); + }); + + const fileRow = screen.getByText('file1.txt'); + await user.click(fileRow); + + await waitFor(() => { + expect(mockOnChange).toHaveBeenCalledWith('/test/directory/file1.txt'); + }); + + await waitFor(() => { + expect(screen.queryByText('Choose a file')).not.toBeInTheDocument(); + }); + }); + + it('should close modal when cancel is clicked', async () => { + const user = userEvent.setup(); + render(); + + const button = screen.getByRole('button', { name: 'Choose' }); + await user.click(button); + + await waitFor(() => { + expect(screen.getByText('Choose a file')).toBeInTheDocument(); + }); + + const cancelButton = screen.getByRole('button', { name: 'Cancel' }); + await user.click(cancelButton); + + await waitFor(() => { + expect(screen.queryByText('Choose a file')).not.toBeInTheDocument(); + }); + }); + + it('should use root path "/" as default sourcePath when value is empty', async () => { + const user = userEvent.setup(); + render(); + + const button = screen.getByRole('button', { name: 'Choose' }); + await user.click(button); + + await waitFor(() => { + expect(screen.getByText('Choose a file')).toBeInTheDocument(); + }); + + await waitFor(() => { + expect(mockUseLoadData).toHaveBeenCalledWith( + expect.any(String), + expect.objectContaining({ + params: expect.objectContaining({ + path: '/' + }) + }) + ); + }); + }); + + it('should use existing value as sourcePath when available', async () => { + const user = userEvent.setup(); + render(); + + const button = screen.getByRole('button', { name: 'Choose' }); + await user.click(button); + + await waitFor(() => { + expect(screen.getByText('Choose a file')).toBeInTheDocument(); + }); + + await waitFor(() => { + expect(mockUseLoadData).toHaveBeenCalledWith( + expect.any(String), + expect.objectContaining({ + params: expect.objectContaining({ + path: '/existing/path' + }) + }) + ); + }); + }); +}); diff --git a/desktop/core/src/desktop/js/reactComponents/FormInput/FileChooserInput/FileChooserInput.tsx b/desktop/core/src/desktop/js/reactComponents/FormInput/FileChooserInput/FileChooserInput.tsx new file mode 100644 index 00000000000..c8cc14f02d8 --- /dev/null +++ b/desktop/core/src/desktop/js/reactComponents/FormInput/FileChooserInput/FileChooserInput.tsx @@ -0,0 +1,82 @@ +// Licensed to Cloudera, Inc. under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. Cloudera, Inc. licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +import React, { useState } from 'react'; +import Input from 'cuix/dist/components/Input'; +import PrimaryButton from 'cuix/dist/components/Button/PrimaryButton'; +import { i18nReact } from '../../../utils/i18nReact'; +import FileChooserModal from '../../../apps/storageBrowser/FileChooserModal/FileChooserModal'; +import './FileChooserInput.scss'; + +export interface FileChooserInputProps { + value?: string; + onChange: (value: string) => void; + placeholder?: string; + error?: boolean; + name?: string; +} + +const FileChooserInput: React.FC = ({ + value, + onChange, + placeholder, + error, + name +}) => { + const { t } = i18nReact.useTranslation(); + const [showModal, setShowModal] = useState(false); + const [selectedPath, setSelectedPath] = useState(value); + + const handleFileChoose = async (selectedPath: string) => { + onChange(selectedPath); + setSelectedPath(selectedPath); + setShowModal(false); + }; + + return ( + <> +
+ onChange(e.target.value)} + placeholder={placeholder ? t(placeholder) : undefined} + status={error ? 'error' : undefined} + name={name} + /> + setShowModal(true)} + className="hue-form-input__file-chooser__button" + > + {t('Choose')} + +
+ {showModal && ( + setShowModal(false)} + onSubmit={handleFileChoose} + title={t('Choose a file')} + sourcePath={selectedPath || '/'} + isFileSelectionAllowed={true} + submitText={t('Select')} + cancelText={t('Cancel')} + /> + )} + + ); +}; + +export default FileChooserInput; diff --git a/desktop/core/src/desktop/js/reactComponents/FormInput/FormInput.tsx b/desktop/core/src/desktop/js/reactComponents/FormInput/FormInput.tsx index f741cd02fb5..654116c33d1 100644 --- a/desktop/core/src/desktop/js/reactComponents/FormInput/FormInput.tsx +++ b/desktop/core/src/desktop/js/reactComponents/FormInput/FormInput.tsx @@ -20,6 +20,7 @@ import Select from 'cuix/dist/components/Select'; import { InfoCircleOutlined } from '@ant-design/icons'; import { i18nReact } from '../../utils/i18nReact'; import { Form, Radio, Tooltip, Input as AntdInput } from 'antd'; +import FileChooserInput from './FileChooserInput/FileChooserInput'; import './FormInput.scss'; export enum FieldType { @@ -27,7 +28,8 @@ export enum FieldType { INPUT = 'input', TEXTAREA = 'textarea', SELECT = 'select', - RADIO = 'radio' + RADIO = 'radio', + FILECHOOSER = 'fileChooser' } export interface FieldOption { @@ -164,6 +166,17 @@ const FormInput = ({ ); + case FieldType.FILECHOOSER: + return ( + onChange(field.name, newValue as T)} + placeholder={field.placeholder} + error={error} + name={field.name} + /> + ); + default: return <>; } From d3f23925442d53506ebde93d943e149a823eb399 Mon Sep 17 00:00:00 2001 From: Ram Prasad Agarwal Date: Wed, 15 Oct 2025 23:33:43 +0530 Subject: [PATCH 3/6] refactor: update FileChooserInput to use relative import and conditional showModal toggle --- .../FormInput/FileChooserInput/FileChooserInput.scss | 2 +- .../FormInput/FileChooserInput/FileChooserInput.tsx | 7 +++++-- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/desktop/core/src/desktop/js/reactComponents/FormInput/FileChooserInput/FileChooserInput.scss b/desktop/core/src/desktop/js/reactComponents/FormInput/FileChooserInput/FileChooserInput.scss index b05f9ae6912..f923e1ab27e 100644 --- a/desktop/core/src/desktop/js/reactComponents/FormInput/FileChooserInput/FileChooserInput.scss +++ b/desktop/core/src/desktop/js/reactComponents/FormInput/FileChooserInput/FileChooserInput.scss @@ -14,7 +14,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -@use '../../../components/styles/variables' as vars; +@use 'variables' as vars; .antd.cuix { .hue-form-input__file-chooser { diff --git a/desktop/core/src/desktop/js/reactComponents/FormInput/FileChooserInput/FileChooserInput.tsx b/desktop/core/src/desktop/js/reactComponents/FormInput/FileChooserInput/FileChooserInput.tsx index c8cc14f02d8..33075ca6b4b 100644 --- a/desktop/core/src/desktop/js/reactComponents/FormInput/FileChooserInput/FileChooserInput.tsx +++ b/desktop/core/src/desktop/js/reactComponents/FormInput/FileChooserInput/FileChooserInput.tsx @@ -43,7 +43,10 @@ const FileChooserInput: React.FC = ({ const handleFileChoose = async (selectedPath: string) => { onChange(selectedPath); setSelectedPath(selectedPath); - setShowModal(false); + + if (showModal) { + setShowModal(false); + } }; return ( @@ -51,7 +54,7 @@ const FileChooserInput: React.FC = ({
onChange(e.target.value)} + onChange={e => handleFileChoose(e.target.value)} placeholder={placeholder ? t(placeholder) : undefined} status={error ? 'error' : undefined} name={name} From e7b05a6e229fbd795e4a8a5fd2a32cc552187181 Mon Sep 17 00:00:00 2001 From: Ram Prasad Agarwal Date: Wed, 15 Oct 2025 23:44:25 +0530 Subject: [PATCH 4/6] fix: remove unnecessary string conversion in FormInput FileChooserInput value --- .../core/src/desktop/js/reactComponents/FormInput/FormInput.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/desktop/core/src/desktop/js/reactComponents/FormInput/FormInput.tsx b/desktop/core/src/desktop/js/reactComponents/FormInput/FormInput.tsx index 654116c33d1..5fb6cd97952 100644 --- a/desktop/core/src/desktop/js/reactComponents/FormInput/FormInput.tsx +++ b/desktop/core/src/desktop/js/reactComponents/FormInput/FormInput.tsx @@ -169,7 +169,7 @@ const FormInput = ({ case FieldType.FILECHOOSER: return ( onChange(field.name, newValue as T)} placeholder={field.placeholder} error={error} From 5ea919b5e37ab89db41530dc028adbd86e067aff Mon Sep 17 00:00:00 2001 From: Ram Prasad Agarwal Date: Tue, 21 Oct 2025 22:57:56 +0530 Subject: [PATCH 5/6] revert: unwanted changes --- .../FileChooserModal/FileChooserModal.tsx | 203 ++++++++++-------- 1 file changed, 108 insertions(+), 95 deletions(-) diff --git a/desktop/core/src/desktop/js/apps/storageBrowser/FileChooserModal/FileChooserModal.tsx b/desktop/core/src/desktop/js/apps/storageBrowser/FileChooserModal/FileChooserModal.tsx index c03295f974d..de8e88e81b1 100644 --- a/desktop/core/src/desktop/js/apps/storageBrowser/FileChooserModal/FileChooserModal.tsx +++ b/desktop/core/src/desktop/js/apps/storageBrowser/FileChooserModal/FileChooserModal.tsx @@ -121,25 +121,32 @@ const FileChooserModal = ({ [setSearchTerm] ); - const columnsConfig: ColumnProps[] = [ - { - dataIndex: 'name', - key: 'name', - render: (_, record: FileChooserTableData) => ( - - - {record.type === BrowserViewType.dir ? : } - - - {record.name} - - - ) + const getColumns = (file: FileChooserTableData) => { + const columns: ColumnProps[] = []; + for (const key of Object.keys(file)) { + const column: ColumnProps = { + dataIndex: key, + key: `${key}` + }; + if (key === 'name') { + column.render = (_, record: FileChooserTableData) => ( + + + {record.type === BrowserViewType.dir ? : } + + + {record.name} + + + ); + } + columns.push(column); } - ]; + return columns.filter(col => col.dataIndex !== 'type' && col.dataIndex !== 'path'); + }; const onRowClicked = (record: FileChooserTableData) => { return { @@ -236,90 +243,96 @@ const FileChooserModal = ({ } ]; - if (!showModal) { - return <>; - } + const TableContent = ( + + + loading={loading && !polling} + data={tableData} + isDynamicHeight + rowKey={r => `${r.path}__${r.type}__${r.name}`} + locale={locale} + columns={getColumns(tableData[0] ?? {})} + rowClassName={record => + record.type === BrowserViewType.file && !isFileSelectionAllowed + ? classNames('hue-filechooser-modal__table-row', 'disabled-row') + : 'hue-filechooser-modal__table-row' + } + onRowClick={onRowClicked} + showHeader={false} + /> + + ); return ( - setShowCreateFolderModal(true)} - cancelText={cancelText} - onCancel={onClose} - > -
-
- -
- { - handleSearch(event.target.value); - }} - /> - - - - loading={loading && !polling} - data={tableData} - isDynamicHeight - rowKey={r => `${r.path}__${r.type}__${r.name}`} - locale={locale} - columns={columnsConfig} - rowClassName={record => - record.type === BrowserViewType.file && !isFileSelectionAllowed - ? classNames('hue-filechooser-modal__table-row', 'disabled-row') - : 'hue-filechooser-modal__table-row' - } - onRowClick={onRowClicked} - showHeader={false} - /> - - -
- - {showCreateFolderModal && ( - { - setShowCreateFolderModal(false); - setCreateFolderValue(''); - }} - onPrimaryClick={handleCreate} + <> + {showModal && ( + setShowCreateFolderModal(true)} + cancelText={cancelText} + onCancel={onClose} > - {/* TODO: Refactor CreateAndUpload to reuse */} - +
+
+ +
handleCreate()} - ref={createFolderInputRef} - onChange={e => { - setCreateFolderValue(e.target.value); + className="hue-filechooser-modal__search" + placeholder={t('Search')} + allowClear={true} + onChange={event => { + handleSearch(event.target.value); }} /> - - + {isUploadEnabled ? ( + {TableContent} + ) : ( + TableContent + )} +
+ + {showCreateFolderModal && ( + { + setShowCreateFolderModal(false); + setCreateFolderValue(''); + }} + onPrimaryClick={handleCreate} + > + {/* TODO: Refactor CreateAndUpload to reuse */} + + handleCreate()} + ref={createFolderInputRef} + onChange={e => { + setCreateFolderValue(e.target.value); + }} + /> + + + )} +
)} -
+ ); }; From 3b07c8f6796457e0e8e34f07aab520988acbba87 Mon Sep 17 00:00:00 2001 From: Ram Prasad Agarwal Date: Tue, 21 Oct 2025 22:58:04 +0530 Subject: [PATCH 6/6] fix: simplify CSS selector in FileChooserInput --- .../FormInput/FileChooserInput/FileChooserInput.scss | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/desktop/core/src/desktop/js/reactComponents/FormInput/FileChooserInput/FileChooserInput.scss b/desktop/core/src/desktop/js/reactComponents/FormInput/FileChooserInput/FileChooserInput.scss index f923e1ab27e..77d6d3e8bbd 100644 --- a/desktop/core/src/desktop/js/reactComponents/FormInput/FileChooserInput/FileChooserInput.scss +++ b/desktop/core/src/desktop/js/reactComponents/FormInput/FileChooserInput/FileChooserInput.scss @@ -22,7 +22,7 @@ align-items: stretch; position: relative; - .cuix.input { + input { flex: 1; border-top-right-radius: 0; border-bottom-right-radius: 0;