fix(nextcloud-files): IN-07 Ablegen auf offene Dialoge und Menues wird ignoriert
- bei offenem Dialog oder Menue (oder einem Ziel darin) zeigt die Ansicht keine Ablage-Markierung und laedt nichts hoch; das Ereignis wird trotzdem abgefangen, damit der Browser die Datei nicht selbst oeffnet - ein bereits behandeltes Ablegen (defaultPrevented) wird nicht hochgeladen Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
import {
|
||||
act,
|
||||
cleanup,
|
||||
createEvent,
|
||||
fireEvent,
|
||||
render as rtlRender,
|
||||
screen,
|
||||
@@ -549,6 +550,35 @@ describe('FileBrowser — Ziehen und Ablegen', () => {
|
||||
expect(screen.queryByTestId('nc-files-drop-overlay')).toBeNull();
|
||||
});
|
||||
|
||||
it('Ablegen bei offenem Dialog wird ignoriert, ohne dass der Browser die Datei oeffnet (IN-07)', async () => {
|
||||
await mount();
|
||||
fireEvent.click(screen.getByRole('button', { name: 'Neuer Ordner' }));
|
||||
const input = await screen.findByLabelText('Name des Ordners');
|
||||
const dt = files([new File(['x'], 'neu.txt')]);
|
||||
fireEvent.dragEnter(input, { dataTransfer: dt });
|
||||
expect(screen.queryByTestId('nc-files-drop-overlay')).toBeNull();
|
||||
const over = createEvent.dragOver(input, { dataTransfer: dt });
|
||||
fireEvent(input, over);
|
||||
expect(over.defaultPrevented).toBe(true);
|
||||
const drop = createEvent.drop(input, { dataTransfer: dt });
|
||||
fireEvent(input, drop);
|
||||
expect(drop.defaultPrevented).toBe(true);
|
||||
await act(async () => {});
|
||||
expect(mockUpload).not.toHaveBeenCalled();
|
||||
expect(screen.queryByTestId('nc-files-drop-overlay')).toBeNull();
|
||||
});
|
||||
|
||||
it('ein schon behandeltes Ablegen (defaultPrevented) wird nicht hochgeladen', async () => {
|
||||
await mount();
|
||||
const dt = files([new File(['x'], 'neu.txt')]);
|
||||
const row = screen.getByRole('row', { name: 'Projekte' });
|
||||
const drop = createEvent.drop(row, { dataTransfer: dt });
|
||||
drop.preventDefault();
|
||||
fireEvent(row, drop);
|
||||
await act(async () => {});
|
||||
expect(mockUpload).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('ein Ziehen ohne Dateien (markierter Text) wird ignoriert', async () => {
|
||||
await mount();
|
||||
const dt = { types: ['text/plain'], files: [], items: [] };
|
||||
|
||||
@@ -417,9 +417,18 @@ export function FileBrowser({
|
||||
return () => window.removeEventListener('beforeunload', warn);
|
||||
}, [anyActive]);
|
||||
|
||||
// Ziehen und Ablegen: am Fenster, mit Tiefenzaehler; nur Ziehen mit Dateien zaehlt.
|
||||
// Ziehen und Ablegen: am Fenster, mit Tiefenzaehler; nur Ziehen mit Dateien zaehlt. Ist ein
|
||||
// Dialog oder Menue offen (oder liegt das Ziel darin), wird nichts abgelegt (IN-07): der
|
||||
// Browser darf die Datei dann aber auch nicht selbst oeffnen.
|
||||
const dialogOpenRef = useRef(false);
|
||||
dialogOpenRef.current = dialog !== null || menu !== null;
|
||||
useEffect(() => {
|
||||
let depth = 0;
|
||||
const blocked = (e: DragEvent): boolean => {
|
||||
if (dialogOpenRef.current) return true;
|
||||
const el = e.target instanceof Element ? e.target : null;
|
||||
return !!el?.closest('[role="dialog"], [role="alertdialog"], [role="menu"]');
|
||||
};
|
||||
const targetOf = (e: DragEvent): string => {
|
||||
const el = e.target instanceof Element ? e.target.closest('[data-drop-folder]') : null;
|
||||
return el?.getAttribute('data-drop-folder') ?? pathRef.current;
|
||||
@@ -427,11 +436,16 @@ export function FileBrowser({
|
||||
const onEnter = (e: DragEvent) => {
|
||||
if (!hasFiles(e.dataTransfer)) return;
|
||||
depth += 1;
|
||||
setDropTarget(targetOf(e));
|
||||
setDropTarget(blocked(e) ? null : targetOf(e));
|
||||
};
|
||||
const onOver = (e: DragEvent) => {
|
||||
if (!hasFiles(e.dataTransfer)) return;
|
||||
e.preventDefault();
|
||||
if (blocked(e)) {
|
||||
if (e.dataTransfer) e.dataTransfer.dropEffect = 'none';
|
||||
setDropTarget(null);
|
||||
return;
|
||||
}
|
||||
if (e.dataTransfer) e.dataTransfer.dropEffect = 'copy';
|
||||
const target = targetOf(e);
|
||||
setDropTarget((prev) => (prev === target ? prev : target));
|
||||
@@ -443,10 +457,13 @@ export function FileBrowser({
|
||||
};
|
||||
const onDrop = (e: DragEvent) => {
|
||||
if (!hasFiles(e.dataTransfer) || !e.dataTransfer) return;
|
||||
// Ein Bestandteil der Seite hat das Ablegen schon selbst behandelt.
|
||||
const handled = e.defaultPrevented;
|
||||
e.preventDefault();
|
||||
depth = 0;
|
||||
const target = targetOf(e);
|
||||
setDropTarget(null);
|
||||
if (handled || blocked(e)) return;
|
||||
const target = targetOf(e);
|
||||
const snapshot = snapshotDrop(e.dataTransfer);
|
||||
void collectDrop(snapshot).then((result) => {
|
||||
if (result.problem) {
|
||||
|
||||
Reference in New Issue
Block a user