Skip to content
Merged
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 @@ -10,6 +10,7 @@ and this project adheres to

- ✨(frontend) Add "Copy link to block" feature #2547
- ✨(frontend) add word count to doc header toolbox #2549
- ✨(frontend) add find and replace feature to the editor #2570

### Changed

Expand Down
61 changes: 54 additions & 7 deletions src/frontend/apps/e2e/__tests__/app-impress/doc-editor.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -622,13 +622,7 @@ test.describe('Doc Editor', () => {
page,
browserName,
}) => {
const [docTitle] = await createDoc(
page,
'doc-viewport-test',
browserName,
1,
);
await verifyDocName(page, docTitle);
await createDoc(page, 'doc-viewport-test', browserName, 1);

const editor = await writeInEditor({
page,
Expand Down Expand Up @@ -658,6 +652,59 @@ test.describe('Doc Editor', () => {
await expect(editor.getByText('Mobile Text')).toBeVisible();
});

test('it searches and replaces occurrences', async ({
page,
browserName,
}) => {
await createDoc(page, 'doc-search-replace', browserName);

const editor = await writeInEditor({
page,
text: 'World',
});

await writeInEditor({
page,
text: 'Hello World - Hello World',
});

// Open the find and replace panel
await page.keyboard.press('Control+f');

// Search for "Hello" and check that the occurrences are highlighted
await page.getByRole('textbox', { name: 'Find in document' }).fill('Hello');
await expect(page.getByText('1 / 2')).toBeVisible();
await expect(
editor
.locator('.find-and-replace-result-current')
.first()
.getByText('Hello'),
).toBeVisible();
await expect(editor.locator('.find-and-replace-result')).toHaveCount(2);

await page.getByRole('button', { name: 'Next match' }).click();
await expect(page.getByText('2 / 2')).toBeVisible();

await page.keyboard.press('Escape');

// Select World then press Ctrl+f to check if the selected text is prefilled in the find input
await page.getByText('World').first().selectText();
await page.keyboard.press('Control+f');
await expect(
page.getByRole('textbox', { name: 'Find in document' }),
).toHaveValue('World');
Comment thread
AntoLC marked this conversation as resolved.

// Replace occurrences
await page.getByRole('button', { name: 'Next match' }).click();
await page.getByRole('button', { name: 'Toggle replace' }).click();
await page.getByRole('textbox', { name: 'Replace with' }).fill('Docs');
await page.getByRole('button', { name: 'Replace', exact: true }).click();
await expect(editor.getByText('Hello Docs - Hello World')).toBeVisible();
await page.getByRole('button', { name: 'Replace all' }).click();
await expect(editor.getByText('Docs', { exact: true })).toBeVisible();
await expect(editor.getByText('Hello Docs - Hello Docs')).toBeVisible();
});

test('it checks "Copy link to block" feature', async ({
page,
browserName,
Expand Down
1 change: 1 addition & 0 deletions src/frontend/apps/impress/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@
"@react-pdf/renderer": "4.3.1",
"@sentry/nextjs": "10.69.0",
"@tanstack/react-query": "5.101.4",
"@tiptap/extension-find-and-replace": "3.29.2",
"@tiptap/extensions": "*",
"ai": "6.0.205",
"canvg": "4.0.3",
Expand Down
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Original file line number Diff line number Diff line change
@@ -1,15 +1,25 @@
import { Button, type ButtonProps } from '@gouvfr-lasuite/ui-components';
import type { ComponentProps } from 'react';

import CloseIcon from '@/assets/icons/ui-kit/x-mark.svg';
import CloseIcon from '@/icons/x-mark.svg';

export const ButtonCloseModal = (props: ButtonProps) => {
type ButtonCloseModalProps = ButtonProps & {
iconProps?: Omit<ComponentProps<typeof CloseIcon>, 'children'>;
};

export const ButtonCloseModal = ({
iconProps,
...props
}: ButtonCloseModalProps) => {
return (
<Button
type="button"
size="small"
color="neutral"
variant="tertiary"
icon={<CloseIcon width="24" height="24" aria-hidden="true" />}
icon={
<CloseIcon width="24" height="24" aria-hidden="true" {...iconProps} />
}
{...props}
/>
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import {
useCreateBlockNote,
} from '@blocknote/react';
import { HocuspocusProvider } from '@hocuspocus/provider';
import { FindAndReplace } from '@tiptap/extension-find-and-replace';
import { useEffect, useMemo, useRef } from 'react';
import { createPortal } from 'react-dom';
import { useTranslation } from 'react-i18next';
Expand All @@ -32,6 +33,7 @@ import {
useCommentSidebarStore,
useComments,
} from '@/docs/doc-comments';
import { DocsFindReplaceStyle } from '@/docs/doc-find-replace/styles';
import { Doc } from '@/docs/doc-management';
import { avatarUrlFromName, useAuth } from '@/features/auth';
import { useRightPanelStore } from '@/features/right-panel/stores/useRightPanelStore';
Expand Down Expand Up @@ -234,6 +236,13 @@ export const BlockNoteEditor = ({ doc, provider }: BlockNoteEditorProps) => {
CommentsExtension({ threadStore, resolveUsers }),
...(aiExtension ? [aiExtension] : []),
],
_tiptapOptions: {
extensions: [
FindAndReplace.configure({
injectCSS: false,
}),
],
},
visualMedia: {
image: {
maxWidth: 760,
Expand Down Expand Up @@ -285,6 +294,7 @@ export const BlockNoteEditor = ({ doc, provider }: BlockNoteEditorProps) => {
canSeeComment={canSeeComment}
currentUserAvatarUrl={currentUserAvatarUrl}
/>
<DocsFindReplaceStyle />
{errorAttachment && (
<Box $margin={{ bottom: 'big', top: 'none', horizontal: 'large' }}>
<TextErrors
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ import { announce } from '@react-aria/live-announcer';
import { useCallback, useEffect } from 'react';
import { useTranslation } from 'react-i18next';

import { useFindReplaceShortcut } from '@/docs/doc-find-replace/hooks/useFindReplaceShortcut';

import { DocsBlockNoteEditor } from '../types';

const getFormattingShortcutLabel = (
Expand Down Expand Up @@ -53,6 +55,7 @@ export const useShortcuts = (
el: HTMLDivElement | null,
) => {
const { t } = useTranslation();
useFindReplaceShortcut(editor);

const handleFormattingShortcut = useCallback(
(event: KeyboardEvent) => {
Expand Down
Loading
Loading