🛂(frontend) block drag n drop when not desktop
Scrolling on mobile devices was causing issues with drag and drop functionality, documents were being moved unintentionally. This commit disables drag and drop on mobile devices to prevent this issue.
This commit is contained in:
@@ -23,6 +23,7 @@ and this project adheres to
|
|||||||
- 🔧(project) change env.d system by using local files #1200
|
- 🔧(project) change env.d system by using local files #1200
|
||||||
- ⚡️(frontend) improve tree stability #1207
|
- ⚡️(frontend) improve tree stability #1207
|
||||||
- ⚡️(frontend) improve accessibility #1232
|
- ⚡️(frontend) improve accessibility #1232
|
||||||
|
- 🛂(frontend) block drag n drop when not desktop #1239
|
||||||
|
|
||||||
### Fixed
|
### Fixed
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import { expect, test } from '@playwright/test';
|
import { expect, test } from '@playwright/test';
|
||||||
|
|
||||||
import { createDoc, mockedListDocs } from './utils-common';
|
import { createDoc, mockedListDocs } from './utils-common';
|
||||||
|
import { createRootSubPage } from './utils-sub-pages';
|
||||||
|
|
||||||
test.describe('Doc grid dnd', () => {
|
test.describe('Doc grid dnd', () => {
|
||||||
test('it creates a doc', async ({ page, browserName }) => {
|
test('it creates a doc', async ({ page, browserName }) => {
|
||||||
@@ -165,6 +166,40 @@ test.describe('Doc grid dnd', () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test.describe('Doc grid dnd mobile', () => {
|
||||||
|
test.use({ viewport: { width: 500, height: 1200 } });
|
||||||
|
|
||||||
|
test('DND is deactivated on mobile', async ({ page, browserName }) => {
|
||||||
|
await page.goto('/');
|
||||||
|
|
||||||
|
const docsGrid = page.getByTestId('docs-grid');
|
||||||
|
await expect(page.getByTestId('docs-grid')).toBeVisible();
|
||||||
|
await expect(page.getByTestId('grid-loader')).toBeHidden();
|
||||||
|
|
||||||
|
await expect(docsGrid.getByRole('row').first()).toBeVisible();
|
||||||
|
await expect(docsGrid.locator('.--docs--grid-droppable')).toHaveCount(0);
|
||||||
|
|
||||||
|
await createDoc(page, 'Draggable doc mobile', browserName, 1, false, true);
|
||||||
|
|
||||||
|
await createRootSubPage(
|
||||||
|
page,
|
||||||
|
browserName,
|
||||||
|
'Draggable doc mobile child',
|
||||||
|
true,
|
||||||
|
);
|
||||||
|
|
||||||
|
await page
|
||||||
|
.getByRole('button', { name: 'Open the header menu' })
|
||||||
|
.getByText('menu')
|
||||||
|
.click();
|
||||||
|
|
||||||
|
await expect(page.locator('.--docs-sub-page-item').first()).toHaveAttribute(
|
||||||
|
'draggable',
|
||||||
|
'false',
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
const data = [
|
const data = [
|
||||||
{
|
{
|
||||||
id: 'can-drop-and-drag',
|
id: 'can-drop-and-drag',
|
||||||
|
|||||||
@@ -79,15 +79,23 @@ export const createDoc = async (
|
|||||||
browserName: string,
|
browserName: string,
|
||||||
length: number = 1,
|
length: number = 1,
|
||||||
isChild: boolean = false,
|
isChild: boolean = false,
|
||||||
|
isMobile: boolean = false,
|
||||||
) => {
|
) => {
|
||||||
const randomDocs = randomName(docName, browserName, length);
|
const randomDocs = randomName(docName, browserName, length);
|
||||||
|
|
||||||
for (let i = 0; i < randomDocs.length; i++) {
|
for (let i = 0; i < randomDocs.length; i++) {
|
||||||
if (!isChild) {
|
if (!isChild && !isMobile) {
|
||||||
const header = page.locator('header').first();
|
const header = page.locator('header').first();
|
||||||
await header.locator('h2').getByText('Docs').click();
|
await header.locator('h2').getByText('Docs').click();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (isMobile) {
|
||||||
|
await page
|
||||||
|
.getByRole('button', { name: 'Open the header menu' })
|
||||||
|
.getByText('menu')
|
||||||
|
.click();
|
||||||
|
}
|
||||||
|
|
||||||
await page
|
await page
|
||||||
.getByRole('button', {
|
.getByRole('button', {
|
||||||
name: 'New doc',
|
name: 'New doc',
|
||||||
|
|||||||
@@ -10,7 +10,15 @@ export const createRootSubPage = async (
|
|||||||
page: Page,
|
page: Page,
|
||||||
browserName: string,
|
browserName: string,
|
||||||
docName: string,
|
docName: string,
|
||||||
|
isMobile: boolean = false,
|
||||||
) => {
|
) => {
|
||||||
|
if (isMobile) {
|
||||||
|
await page
|
||||||
|
.getByRole('button', { name: 'Open the header menu' })
|
||||||
|
.getByText('menu')
|
||||||
|
.click();
|
||||||
|
}
|
||||||
|
|
||||||
// Get response
|
// Get response
|
||||||
const responsePromise = waitForResponseCreateDoc(page);
|
const responsePromise = waitForResponseCreateDoc(page);
|
||||||
await clickOnAddRootSubPage(page);
|
await clickOnAddRootSubPage(page);
|
||||||
@@ -18,6 +26,13 @@ export const createRootSubPage = async (
|
|||||||
expect(response.ok()).toBeTruthy();
|
expect(response.ok()).toBeTruthy();
|
||||||
const subPageJson = (await response.json()) as { id: string };
|
const subPageJson = (await response.json()) as { id: string };
|
||||||
|
|
||||||
|
if (isMobile) {
|
||||||
|
await page
|
||||||
|
.getByRole('button', { name: 'Open the header menu' })
|
||||||
|
.getByText('menu')
|
||||||
|
.click();
|
||||||
|
}
|
||||||
|
|
||||||
// Get doc tree
|
// Get doc tree
|
||||||
const docTree = page.getByTestId('doc-tree');
|
const docTree = page.getByTestId('doc-tree');
|
||||||
await expect(docTree).toBeVisible();
|
await expect(docTree).toBeVisible();
|
||||||
@@ -29,6 +44,13 @@ export const createRootSubPage = async (
|
|||||||
await expect(subPageItem).toBeVisible();
|
await expect(subPageItem).toBeVisible();
|
||||||
await subPageItem.click();
|
await subPageItem.click();
|
||||||
|
|
||||||
|
if (isMobile) {
|
||||||
|
await page
|
||||||
|
.getByRole('button', { name: 'Open the header menu' })
|
||||||
|
.getByText('close')
|
||||||
|
.click();
|
||||||
|
}
|
||||||
|
|
||||||
// Update sub page name
|
// Update sub page name
|
||||||
const randomDocs = randomName(docName, browserName, 1);
|
const randomDocs = randomName(docName, browserName, 1);
|
||||||
await updateDocTitle(page, randomDocs[0]);
|
await updateDocTitle(page, randomDocs[0]);
|
||||||
|
|||||||
@@ -71,6 +71,7 @@ export const DocSubPageItem = (props: TreeViewNodeProps<Doc>) => {
|
|||||||
return (
|
return (
|
||||||
<Box
|
<Box
|
||||||
className="--docs-sub-page-item"
|
className="--docs-sub-page-item"
|
||||||
|
draggable={doc.abilities.move && isDesktop}
|
||||||
$position="relative"
|
$position="relative"
|
||||||
$css={css`
|
$css={css`
|
||||||
background-color: ${actionsOpen
|
background-color: ${actionsOpen
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ import {
|
|||||||
OpenMap,
|
OpenMap,
|
||||||
TreeView,
|
TreeView,
|
||||||
TreeViewMoveResult,
|
TreeViewMoveResult,
|
||||||
|
useResponsive,
|
||||||
useTreeContext,
|
useTreeContext,
|
||||||
} from '@gouvfr-lasuite/ui-kit';
|
} from '@gouvfr-lasuite/ui-kit';
|
||||||
import { useRouter } from 'next/navigation';
|
import { useRouter } from 'next/navigation';
|
||||||
@@ -29,6 +30,7 @@ export const DocTree = ({ currentDoc }: DocTreeProps) => {
|
|||||||
const [rootActionsOpen, setRootActionsOpen] = useState(false);
|
const [rootActionsOpen, setRootActionsOpen] = useState(false);
|
||||||
const treeContext = useTreeContext<Doc | null>();
|
const treeContext = useTreeContext<Doc | null>();
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
|
const { isDesktop } = useResponsive();
|
||||||
|
|
||||||
const [initialOpenState, setInitialOpenState] = useState<OpenMap | undefined>(
|
const [initialOpenState, setInitialOpenState] = useState<OpenMap | undefined>(
|
||||||
undefined,
|
undefined,
|
||||||
@@ -243,13 +245,13 @@ export const DocTree = ({ currentDoc }: DocTreeProps) => {
|
|||||||
canDrop={({ parentNode }) => {
|
canDrop={({ parentNode }) => {
|
||||||
const parentDoc = parentNode?.data.value as Doc;
|
const parentDoc = parentNode?.data.value as Doc;
|
||||||
if (!parentDoc) {
|
if (!parentDoc) {
|
||||||
return currentDoc.abilities.move;
|
return currentDoc.abilities.move && isDesktop;
|
||||||
}
|
}
|
||||||
return parentDoc.abilities.move;
|
return parentDoc.abilities.move && isDesktop;
|
||||||
}}
|
}}
|
||||||
canDrag={(node) => {
|
canDrag={(node) => {
|
||||||
const doc = node.value as Doc;
|
const doc = node.value as Doc;
|
||||||
return doc.abilities.move;
|
return doc.abilities.move && isDesktop;
|
||||||
}}
|
}}
|
||||||
rootNodeId={treeContext.root.id}
|
rootNodeId={treeContext.root.id}
|
||||||
renderNode={DocSubPageItem}
|
renderNode={DocSubPageItem}
|
||||||
|
|||||||
@@ -51,7 +51,9 @@ type DocGridContentListProps = {
|
|||||||
docs: Doc[];
|
docs: Doc[];
|
||||||
};
|
};
|
||||||
|
|
||||||
export const DocGridContentList = ({ docs }: DocGridContentListProps) => {
|
export const DraggableDocGridContentList = ({
|
||||||
|
docs,
|
||||||
|
}: DocGridContentListProps) => {
|
||||||
const { mutateAsync: handleMove, isError } = useMoveDoc();
|
const { mutateAsync: handleMove, isError } = useMoveDoc();
|
||||||
const queryClient = useQueryClient();
|
const queryClient = useQueryClient();
|
||||||
const modalConfirmation = useModal();
|
const modalConfirmation = useModal();
|
||||||
@@ -223,7 +225,7 @@ export const DocGridContentList = ({ docs }: DocGridContentListProps) => {
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
interface DocGridItemProps {
|
interface DraggableDocGridItemProps {
|
||||||
doc: Doc;
|
doc: Doc;
|
||||||
dragMode: boolean;
|
dragMode: boolean;
|
||||||
canDrag: boolean;
|
canDrag: boolean;
|
||||||
@@ -235,7 +237,7 @@ export const DraggableDocGridItem = ({
|
|||||||
dragMode,
|
dragMode,
|
||||||
canDrag,
|
canDrag,
|
||||||
updateCanDrop,
|
updateCanDrop,
|
||||||
}: DocGridItemProps) => {
|
}: DraggableDocGridItemProps) => {
|
||||||
const canDrop = doc.abilities.move;
|
const canDrop = doc.abilities.move;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@@ -252,3 +254,13 @@ export const DraggableDocGridItem = ({
|
|||||||
</Droppable>
|
</Droppable>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const DocGridContentList = ({ docs }: DocGridContentListProps) => {
|
||||||
|
if (docs.length === 0) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return docs.map((doc) => (
|
||||||
|
<DocsGridItem dragMode={false} doc={doc} key={doc.id} />
|
||||||
|
));
|
||||||
|
};
|
||||||
|
|||||||
@@ -9,7 +9,10 @@ import { useResponsiveStore } from '@/stores';
|
|||||||
|
|
||||||
import { useResponsiveDocGrid } from '../hooks/useResponsiveDocGrid';
|
import { useResponsiveDocGrid } from '../hooks/useResponsiveDocGrid';
|
||||||
|
|
||||||
import { DocGridContentList } from './DocGridContentList';
|
import {
|
||||||
|
DocGridContentList,
|
||||||
|
DraggableDocGridContentList,
|
||||||
|
} from './DocGridContentList';
|
||||||
import { DocsGridLoader } from './DocsGridLoader';
|
import { DocsGridLoader } from './DocsGridLoader';
|
||||||
|
|
||||||
type DocsGridProps = {
|
type DocsGridProps = {
|
||||||
@@ -118,7 +121,11 @@ export const DocsGrid = ({
|
|||||||
)}
|
)}
|
||||||
</Box>
|
</Box>
|
||||||
|
|
||||||
<DocGridContentList docs={docs} />
|
{isDesktop ? (
|
||||||
|
<DraggableDocGridContentList docs={docs} />
|
||||||
|
) : (
|
||||||
|
<DocGridContentList docs={docs} />
|
||||||
|
)}
|
||||||
|
|
||||||
{hasNextPage && !loading && (
|
{hasNextPage && !loading && (
|
||||||
<InView
|
<InView
|
||||||
|
|||||||
Reference in New Issue
Block a user