Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ and this project adheres to
- ✨(frontend) Can print a doc #1832
- ✨(backend) manage reconciliation requests for user accounts #1878
- 👷(CI) add GHCR workflow for forked repo testing #1851
- ✨(frontend) Move doc modal #1886
- ⚡️(backend) remove content from Document serializer when asked #1910
- ✨(backend) allow the duplication of subpages #1893
- ✨(backend) Onboarding docs for new users #1891
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,26 @@
import { expect, test } from '@playwright/test';

import { createDoc, mockedListDocs, toggleHeaderMenu } from './utils-common';
import {
createDoc,
getGridRow,
getOtherBrowserName,
mockedListDocs,
toggleHeaderMenu,
verifyDocName,
} from './utils-common';
import { writeInEditor } from './utils-editor';
import {
addNewMember,
connectOtherUserToDoc,
updateShareLink,
} from './utils-share';
import { createRootSubPage } from './utils-sub-pages';

test.describe('Doc grid dnd', () => {
test('it creates a doc', async ({ page, browserName }) => {
test.describe('Doc grid move', () => {
test('it checks drag and drop functionality', async ({
page,
browserName,
}) => {
await page.goto('/');
const header = page.locator('header').first();
await createDoc(page, 'Draggable doc', browserName, 1);
Expand All @@ -29,7 +45,7 @@ test.describe('Doc grid dnd', () => {
await expect(draggableElement).toBeVisible();
await expect(dropZone).toBeVisible();

// Obtenir les positions des éléments
// Get the position of the elements
const draggableBoundingBox = await draggableElement.boundingBox();
const dropZoneBoundingBox = await dropZone.boundingBox();

Expand All @@ -46,7 +62,7 @@ test.describe('Doc grid dnd', () => {
);
await page.mouse.down();

// Déplacer vers la zone cible
// Move to the target zone
await page.mouse.move(
dropZoneBoundingBox.x + dropZoneBoundingBox.width / 2,
dropZoneBoundingBox.y + dropZoneBoundingBox.height / 2,
Expand Down Expand Up @@ -161,6 +177,207 @@ test.describe('Doc grid dnd', () => {

await page.mouse.up();
});

test('it moves a doc from the doc search modal', async ({
page,
browserName,
}) => {
await page.goto('/');

const [titleDoc1] = await createDoc(page, 'Draggable doc', browserName, 1);

const otherBrowserName = getOtherBrowserName(browserName);
await page.getByRole('button', { name: 'Share' }).click();
await addNewMember(page, 0, 'Administrator', otherBrowserName);
await page
.getByRole('dialog')
.getByRole('button', { name: 'close' })
.click();

await page.getByRole('button', { name: 'Back to homepage' }).click();

const [titleDoc2] = await createDoc(page, 'Droppable doc', browserName, 1);
await page.getByRole('button', { name: 'Back to homepage' }).click();

const docsGrid = page.getByTestId('docs-grid');
await expect(docsGrid.getByText(titleDoc1)).toBeVisible();
await expect(docsGrid.getByText(titleDoc2)).toBeVisible();

const row = await getGridRow(page, titleDoc1);
await row.getByText(`more_horiz`).click();

await page.getByRole('menuitem', { name: 'Move into a doc' }).click();

await expect(
page.getByRole('dialog').getByRole('heading', { name: 'Move' }),
).toBeVisible();

const input = page.getByRole('combobox', { name: 'Quick search input' });
await input.click();
await input.fill(titleDoc2);

await expect(page.getByRole('option').getByText(titleDoc2)).toBeVisible();

// Select the first result
await page.keyboard.press('Enter');
// The CTA should get the focus
await page.keyboard.press('Tab');
// Validate the move action
await page.keyboard.press('Enter');

await expect(
page
.getByRole('dialog')
.getByText('it will lose its current access rights'),
).toBeVisible();

await page
.getByRole('dialog')
.getByRole('button', { name: 'Move', exact: true })
.first()
.click();

await expect(docsGrid.getByText(titleDoc1)).toBeHidden();
await docsGrid
.getByRole('link', { name: `Open document ${titleDoc2}` })
.click();

await verifyDocName(page, titleDoc2);

const docTree = page.getByTestId('doc-tree');
await expect(docTree.getByText(titleDoc1)).toBeVisible();
});

test('it proposes an access request when moving a doc without sufficient permissions', async ({
page,
browserName,
}) => {
test.slow();
await page.goto('/');

const [titleDoc1] = await createDoc(page, 'Move doc', browserName, 1);

const { otherPage, cleanup } = await connectOtherUserToDoc({
docUrl: '/',
browserName,
});

// Another user creates a doc
const [titleDoc2] = await createDoc(otherPage, 'Drop doc', browserName, 1);
await writeInEditor({
page: otherPage,
text: 'Hello world',
});
// Make it public
await otherPage.getByRole('button', { name: 'Share' }).click();
await updateShareLink(otherPage, 'Public');
await otherPage
.getByRole('dialog')
.getByRole('button', { name: 'close' })
.click();
const otherPageUrl = otherPage.url();

// The first user visit the doc to have it in his grid list
await page.goto(otherPageUrl);
await expect(page.getByText('Hello world')).toBeVisible();

await page.waitForTimeout(1000);

await page.getByRole('button', { name: 'Back to homepage' }).click();

const docsGrid = page.getByTestId('docs-grid');
await expect(docsGrid.getByText(titleDoc1)).toBeVisible();
await expect(docsGrid.getByText(titleDoc2)).toBeVisible();

const row = await getGridRow(page, titleDoc1);
await row.getByText(`more_horiz`).click();

await page.getByRole('menuitem', { name: 'Move into a doc' }).click();

await expect(
page.getByRole('dialog').getByRole('heading', { name: 'Move' }),
).toBeVisible();

const input = page.getByRole('combobox', { name: 'Quick search input' });
await input.click();
await input.fill(titleDoc2);

await expect(page.getByRole('option').getByText(titleDoc2)).toBeVisible();

// Select the first result
await page.keyboard.press('Enter');
// The CTA should get the focus
await page.keyboard.press('Tab');
// Validate the move action
await page.keyboard.press('Enter');

// Request access modal should be visible
await expect(
page
.getByRole('dialog')
.getByText(
'You need edit access to the destination. Request access, then try again.',
),
).toBeVisible();

await page
.getByRole('dialog')
.getByRole('button', { name: 'Request access', exact: true })
.first()
.click();

// The other user should receive the access request and be able to approve it
await otherPage.getByRole('button', { name: 'Share' }).click();
await expect(otherPage.getByText('Access Requests')).toBeVisible();
await expect(otherPage.getByText(`E2E ${browserName}`)).toBeVisible();

const emailRequest = `user.test@${browserName}.test`;
await expect(otherPage.getByText(emailRequest)).toBeVisible();
const container = otherPage.getByTestId(
`doc-share-access-request-row-${emailRequest}`,
);
await container.getByTestId('doc-role-dropdown').click();
await otherPage.getByRole('menuitem', { name: 'Administrator' }).click();
await container.getByRole('button', { name: 'Approve' }).click();

await expect(otherPage.getByText('Access Requests')).toBeHidden();
await expect(otherPage.getByText('Share with 2 users')).toBeVisible();
await expect(otherPage.getByText(`E2E ${browserName}`)).toBeVisible();

// The first user should now be able to move the doc
await page.reload();
await row.getByText(`more_horiz`).click();

await page.getByRole('menuitem', { name: 'Move into a doc' }).click();

await expect(
page.getByRole('dialog').getByRole('heading', { name: 'Move' }),
).toBeVisible();

await input.click();
await input.fill(titleDoc2);

await expect(page.getByRole('option').getByText(titleDoc2)).toBeVisible();

// Select the first result
await page.keyboard.press('Enter');
// The CTA should get the focus
await page.keyboard.press('Tab');
// Validate the move action
await page.keyboard.press('Enter');

await expect(docsGrid.getByText(titleDoc1)).toBeHidden();
await docsGrid
.getByRole('link', { name: `Open document ${titleDoc2}` })
.click();

await verifyDocName(page, titleDoc2);

const docTree = page.getByTestId('doc-tree');
await expect(docTree.getByText(titleDoc1)).toBeVisible();

await cleanup();
});
});

test.describe('Doc grid dnd mobile', () => {
Expand Down
39 changes: 33 additions & 6 deletions src/frontend/apps/impress/src/components/modal/AlertModal.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
import { Button, Modal, ModalSize } from '@gouvfr-lasuite/cunningham-react';
import { ReactNode } from 'react';
import {
Button,
ButtonProps,
Modal,
ModalProps,
ModalSize,
} from '@gouvfr-lasuite/cunningham-react';
import { ReactNode, useEffect } from 'react';
import { useTranslation } from 'react-i18next';

import { Box } from '../Box';
Expand All @@ -10,10 +16,11 @@ export type AlertModalProps = {
isOpen: boolean;
onClose: () => void;
onConfirm: () => void;
themeCTA?: ButtonProps['color'];
title: string;
cancelLabel?: string;
confirmLabel?: string;
};
} & Partial<ModalProps>;

export const AlertModal = ({
cancelLabel,
Expand All @@ -23,8 +30,26 @@ export const AlertModal = ({
onClose,
onConfirm,
title,
themeCTA,
...props
}: AlertModalProps) => {
const { t } = useTranslation();

/**
* TODO:
* Remove this effect when Cunningham will have this patch released:
* https://github.com/suitenumerique/cunningham/pull/377
*/
useEffect(() => {
const timeout = setTimeout(() => {
const contents = document.querySelectorAll('.c__modal__content');
contents.forEach((content) => {
content.setAttribute('tabindex', '-1');
});
}, 100);
return () => clearTimeout(timeout);
}, []);

return (
<Modal
isOpen={isOpen}
Expand All @@ -43,24 +68,26 @@ export const AlertModal = ({
</Text>
}
rightActions={
<>
<Box $direction="row" $gap="small">
<Button
aria-label={`${t('Cancel')} - ${title}`}
variant="secondary"
fullWidth
autoFocus
onClick={() => onClose()}
>
{cancelLabel ?? t('Cancel')}
</Button>
<Button
aria-label={confirmLabel ?? t('Confirm')}
color="error"
color={themeCTA ?? 'error'}
onClick={onConfirm}
>
{confirmLabel ?? t('Confirm')}
</Button>
</>
</Box>
}
{...props}
>
<Box className="--docs--alert-modal">
<Box>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ export type QuickSearchAction = {

export type QuickSearchData<T> = {
groupName: string;
groupKey?: string;
elements: T[];
emptyString?: string;
startActions?: QuickSearchAction[];
Expand All @@ -30,13 +31,13 @@ export type QuickSearchProps = {
loading?: boolean;
label?: string;
placeholder?: string;
groupKey?: string;
};

export const QuickSearch = ({
onFilter,
inputContent,
inputValue,
loading,
showInput = true,
label,
placeholder,
Expand Down Expand Up @@ -72,10 +73,10 @@ export const QuickSearch = ({
tabIndex={-1}
value={selectedValue}
onValueChange={handleValueChange}
disablePointerSelection
>
{showInput && (
<QuickSearchInput
loading={loading}
withSeparator={hasChildrens(children)}
inputValue={inputValue}
onFilter={onFilter}
Expand Down
Loading
Loading