diff --git a/src/files/FilesPage.js b/src/files/FilesPage.js index d6639a2e4..7b6a6b5bc 100644 --- a/src/files/FilesPage.js +++ b/src/files/FilesPage.js @@ -211,6 +211,7 @@ const FilesPage = ({ failedPins: failedPins || [], filesPathInfo, selected, + modalOpen: modals.show !== null, onSelect: (name, isSelected) => { if (Array.isArray(name)) { if (isSelected) { diff --git a/src/files/files-grid/files-grid.tsx b/src/files/files-grid/files-grid.tsx index b01554d6f..11832ab78 100644 --- a/src/files/files-grid/files-grid.tsx +++ b/src/files/files-grid/files-grid.tsx @@ -34,11 +34,12 @@ interface FilesGridPropsConnected extends FilesGridProps { onSelect: (fileName: string | string[], isSelected: boolean) => void filesIsFetching: boolean selected: string[] + modalOpen: boolean } const FilesGrid = ({ files, pins = [], remotePins = [], pendingPins = [], failedPins = [], filesPathInfo, t, onRemove, onRename, onNavigate, onAddFiles, - onMove, handleContextMenuClick, filesIsFetching, onSetPinning, onDismissFailedPin, selected = [], onSelect + onMove, handleContextMenuClick, filesIsFetching, onSetPinning, onDismissFailedPin, selected = [], onSelect, modalOpen = false }: FilesGridPropsConnected) => { const [focused, setFocused] = useState(null) const filesRefs = useRef>({}) @@ -67,6 +68,11 @@ const FilesGrid = ({ }, [onSelect]) const keyHandler = useCallback((e: KeyboardEvent) => { + // Don't handle keyboard events when a modal is open + if (modalOpen) { + return + } + const focusedFile = focused == null ? null : files.find(el => el.name === focused) gridRef.current?.focus?.() @@ -148,8 +154,7 @@ const FilesGrid = ({ } } } - // eslint-disable-next-line react-hooks/exhaustive-deps - }, [files, focused]) + }, [files, focused, selected, onSelect, onRename, onRemove, onNavigate, handleSelect, modalOpen]) useEffect(() => { if (filesIsFetching) return diff --git a/test/e2e/grid-view.test.js b/test/e2e/grid-view.test.js index 458454053..8d79e909b 100644 --- a/test/e2e/grid-view.test.js +++ b/test/e2e/grid-view.test.js @@ -126,25 +126,40 @@ test.describe('Files grid view', () => { const folder = page.locator('.grid-file[title$="/"], .grid-file[data-type="directory"]').first() const folderName = await folder.getAttribute('title') + // Press ArrowRight to focus the first item await page.keyboard.press('ArrowRight') + // Wait for the focused element to appear using proper Playwright waiting + await page.locator('.grid-file.focused').waitFor({ state: 'visible', timeout: 5000 }) + // Navigate to the folder (may need multiple presses) for (let i = 0; i < 5; i++) { const focusedItem = page.locator('.grid-file.focused') + await focusedItem.waitFor({ state: 'visible', timeout: 5000 }) + const focusedTitle = await focusedItem.getAttribute('title') || '' if (focusedTitle === folderName || focusedTitle.endsWith('/')) { break } await page.keyboard.press('ArrowRight') + // Small wait for focus to update + await page.waitForTimeout(100) } + // Store current URL to detect navigation + const currentUrl = page.url() + // Press Enter to open folder await page.keyboard.press('Enter') - // Verify we're inside a folder (wait for navigation) - await page.waitForTimeout(1000) + // Wait for navigation using proper Playwright methods + await page.waitForFunction( + (url) => window.location.href !== url, + currentUrl, + { timeout: 5000 } + ) // Verify navigation happened (URL changed or breadcrumb updated) - await expect(page.locator('.joyride-files-breadcrumbs')).toContainText(`Files/${folderName}`) + await expect(page.locator('.joyride-files-breadcrumbs')).toContainText(`Files/${folderName}`, { timeout: 5000 }) }) }) diff --git a/test/e2e/settings.test.js b/test/e2e/settings.test.js index c7a7e45e1..33a1691ba 100644 --- a/test/e2e/settings.test.js +++ b/test/e2e/settings.test.js @@ -39,16 +39,29 @@ async function checkClassWithTimeout (page, element, className, maxWaitTime = 16 * @param {string} expectedClass - The expected class after submission. */ async function submitGatewayAndCheck (page, inputElement, submitButton, gatewayURL, expectedClass) { + // Clear the input first to ensure a clean state + await inputElement.click({ clickCount: 3 }) // Select all text await inputElement.fill(gatewayURL) + + // Give time for async validation to complete + await page.waitForTimeout(500) + // Check if the submit button is not null, and click it only if it's available if (submitButton) { const buttonId = await submitButton.evaluate(el => el.id) // Use locator API which handles re-renders automatically const submitBtn = page.locator(`#${buttonId}`) - // Wait for button to be enabled after input validation + + // Wait for button to be visible first + await submitBtn.waitFor({ state: 'visible', timeout: 10000 }) + + // Wait for the button to become enabled (validation must complete) await expect(submitBtn).toBeEnabled({ timeout: 10000 }) + + // Now click the enabled button await submitBtn.click() } + const hasExpectedClass = await checkClassWithTimeout(page, inputElement, expectedClass) expect(hasExpectedClass).toBe(true) } diff --git a/test/helpers/grid.js b/test/helpers/grid.js index 2ec97e331..e835346bd 100644 --- a/test/helpers/grid.js +++ b/test/helpers/grid.js @@ -26,7 +26,7 @@ const selectViewMode = async (page, mode) => { const navigateToFilesPage = async (page) => { await page.goto(webuiUrl + '#/files') await waitForIpfsStats() - await page.waitForSelector('.files-grid, .FilesList') + await page.waitForSelector('.files-grid, .FilesList', { timeout: 60000 }) } /**