Cover Files inline rename validation
This commit is contained in:
parent
167d593245
commit
5c47ca2480
|
|
@ -95,6 +95,23 @@ test.describe('G: Files Plugin', () => {
|
|||
await expect(page.locator('.files-breadcrumb')).not.toContainText('Daily');
|
||||
});
|
||||
|
||||
test('files explorer shows inline rename validation errors', async ({ page }) => {
|
||||
await page.locator('.wt-label').filter({ hasText: 'Project' }).click();
|
||||
await openFilesTool(page);
|
||||
await expect(page.locator('.files-breadcrumb')).toContainText('Project', { timeout: 10000 });
|
||||
|
||||
await page.locator('[data-file-name="project-only.txt"]').click();
|
||||
await page.locator('[data-files-action="rename"]').click();
|
||||
await page.locator('[data-files-rename-input]').fill('bad/name.txt');
|
||||
await page.locator('[data-files-rename-confirm]').click();
|
||||
|
||||
await expect(page.locator('[data-files-rename-error]')).toBeVisible();
|
||||
await expect(page.locator('[data-files-rename-error]')).toContainText('Invalid characters');
|
||||
await expect(page.locator('[data-files-rename-input]')).toBeVisible();
|
||||
await expect(page.locator('[data-file-name="project-only.txt"]')).toBeVisible();
|
||||
await expect(page.locator('[data-file-name="bad"]')).toHaveCount(0);
|
||||
});
|
||||
|
||||
test('files explorer uses labeled controls and no row New Here action', async ({ page }) => {
|
||||
await page.locator('.wt-label').filter({ hasText: 'Project' }).click();
|
||||
await openFilesTool(page);
|
||||
|
|
|
|||
|
|
@ -773,10 +773,14 @@
|
|||
createPanel.appendChild(e('button', { className: 'files-toolbar-btn', onClick: function () { createPanel.style.display = 'none'; } }, ['Cancel']));
|
||||
c.appendChild(createPanel);
|
||||
var renamePanel = e('div', { className: 'files-panel', style: 'display:none' }, []);
|
||||
var renameField = e('div', { style: { display: 'flex', flex: '1', flexDirection: 'column', gap: '.25rem' } }, []);
|
||||
var renameInput = e('input', { className: 'files-rename-input', 'data-files-rename-input': '' }, []);
|
||||
renamePanel.appendChild(renameInput);
|
||||
var renameError = e('div', { 'data-files-rename-error': '', role: 'alert', style: { display: 'none', color: '#ff8a8a', fontSize: '.72rem', lineHeight: '1.2' } }, []);
|
||||
renameField.appendChild(renameInput);
|
||||
renameField.appendChild(renameError);
|
||||
renamePanel.appendChild(renameField);
|
||||
renamePanel.appendChild(e('button', { className: 'files-toolbar-btn', 'data-files-rename-confirm': '', onClick: confirmRename }, ['Rename']));
|
||||
renamePanel.appendChild(e('button', { className: 'files-toolbar-btn', onClick: function () { renamePanel.style.display = 'none'; } }, ['Cancel']));
|
||||
renamePanel.appendChild(e('button', { className: 'files-toolbar-btn', onClick: cancelRename }, ['Cancel']));
|
||||
c.appendChild(renamePanel);
|
||||
function entryByPath(path) { return entries.find(function (item) { return item.relativePath === path; }) || null; }
|
||||
function selectedEntries() { return Object.keys(selected).map(entryByPath).filter(Boolean); }
|
||||
|
|
@ -870,12 +874,22 @@
|
|||
var path = scoped(current ? current + '/' + name : name);
|
||||
(mode === 'folder' ? api.files.createFolder(path) : api.files.writeText(path, '', { createIfMissing: true, overwrite: false })).then(function () { createPanel.style.display = 'none'; load(); });
|
||||
}
|
||||
function startRename(item) { if (!item) return; renaming = item; renameInput.value = item.name; renamePanel.style.display = 'flex'; renameInput.focus(); renameInput.select(); }
|
||||
function setRenameError(message) {
|
||||
renameError.textContent = message || '';
|
||||
renameError.style.display = message ? 'block' : 'none';
|
||||
renameInput.setAttribute('aria-invalid', message ? 'true' : 'false');
|
||||
}
|
||||
function cancelRename() { renaming = null; setRenameError(''); renamePanel.style.display = 'none'; }
|
||||
function startRename(item) { if (!item) return; renaming = item; renameInput.value = item.name; setRenameError(''); renamePanel.style.display = 'flex'; renameInput.focus(); renameInput.select(); }
|
||||
function confirmRename() {
|
||||
if (!renaming) return;
|
||||
var newName = renameInput.value.trim();
|
||||
if (!newName || newName === renaming.name) { cancelRename(); return; }
|
||||
if (/[\\/:*?"<>|\x00-\x1f]/.test(newName)) { setRenameError('Invalid characters in name'); return; }
|
||||
if (newName === '.' || newName === '..' || newName[0] === ' ' || newName[newName.length - 1] === ' ' || newName[newName.length - 1] === '.') { setRenameError('Invalid name'); return; }
|
||||
var to = parent(renaming.relativePath);
|
||||
to = to ? to + '/' + renameInput.value.trim() : renameInput.value.trim();
|
||||
api.files.move(renaming.relativePath, to, { overwrite: false }).then(function () { renamePanel.style.display = 'none'; renaming = null; load(); });
|
||||
to = to ? to + '/' + newName : newName;
|
||||
api.files.move(renaming.relativePath, to, { overwrite: false }).then(function () { cancelRename(); load(); }).catch(function (err) { setRenameError('Error: ' + ((err && err.message) ? err.message : String(err))); });
|
||||
}
|
||||
function trash(item) { if (!item || !window.confirm('Move "' + item.name + '" to trash?')) return; api.files.trash(item.relativePath).then(load); }
|
||||
function trashSelection() { var items = selectedEntries(); if (items.length === 1) return trash(items[0]); if (!items.length || !window.confirm('Move ' + items.length + ' items to trash?')) return; Promise.all(items.map(function (item) { return api.files.trash(item.relativePath); })).then(load); }
|
||||
|
|
@ -918,7 +932,8 @@
|
|||
menu.style.display = 'block'; menu.style.left = x + 'px'; menu.style.top = y + 'px';
|
||||
}
|
||||
createInput.addEventListener('keydown', function (ev) { if (ev.key === 'Enter') confirmCreate(); });
|
||||
renameInput.addEventListener('keydown', function (ev) { if (ev.key === 'Enter') confirmRename(); });
|
||||
renameInput.addEventListener('input', function () { setRenameError(''); });
|
||||
renameInput.addEventListener('keydown', function (ev) { if (ev.key === 'Enter') confirmRename(); if (ev.key === 'Escape') cancelRename(); });
|
||||
list.addEventListener('contextmenu', function (ev) { ev.preventDefault(); var row = ev.target.closest('.files-item'); showMenu(ev.clientX, ev.clientY, row ? entryByPath(row.getAttribute('data-file-path')) : null); });
|
||||
list.addEventListener('dragover', function (ev) { ev.preventDefault(); var row = ev.target.closest('.files-item'); if (row) row.classList.add('files-drag-over'); });
|
||||
list.addEventListener('dragleave', function (ev) { var row = ev.target.closest('.files-item'); if (row) row.classList.remove('files-drag-over'); });
|
||||
|
|
|
|||
Loading…
Reference in New Issue