mirror of
https://github.com/suitenumerique/docs.git
synced 2026-10-18 05:57:38 +02:00
🐛(frontend) keep caption and alignment when replacing an image
The antivirus loader rebuilt the image block with an empty caption and left alignment, so both were lost after a replace. Signed-off-by: Ovgodd <c.gromoff@gmail.com>
This commit is contained in:
+2
-1
@@ -11,7 +11,7 @@ and this project adheres to
|
||||
- 👷(ci) lint, typecheck, build and test the collaboration server (yhub)
|
||||
- 👷(ci) check the load-test tooling: swarm, canary, k6 and dashboards
|
||||
- 👷(ci) cancel the superseded runs of a pull request
|
||||
- ✨(backend) add mention endpoint with cooldown-limited email
|
||||
- ✨(backend) add mention endpoint with cooldown-limited email
|
||||
notification #2447
|
||||
- 🚩(setting) add feature flag on Duplicate with Children #2721
|
||||
- 💄(frontend) redesign 404 error standalone page #2696
|
||||
@@ -93,6 +93,7 @@ and this project adheres to
|
||||
- 🐛(frontend) open search results in a new tab with ctrl/cmd+click #2719
|
||||
- 🐛(docker) pull minio images from pgsty
|
||||
- 🐛(backend) retry the duplicate of a document on a tree path collision
|
||||
- 🐛(frontend) keep caption and alignment when replacing an image #2730
|
||||
- 🐛(frontend) clear callout background on Backspace #2052
|
||||
- 🐛(export) keep image aspect ratio in PDF columns #2670
|
||||
- 🐛(frontend) fix redirect after deleting a document #2706
|
||||
|
||||
+12
-2
@@ -29,6 +29,11 @@ type UploadLoaderPropSchema = {
|
||||
readonly default: '';
|
||||
};
|
||||
readonly blockUploadUrl: { readonly default: '' };
|
||||
readonly blockUploadCaption: { readonly default: '' };
|
||||
readonly blockUploadTextAlignment: {
|
||||
readonly default: 'left';
|
||||
readonly values: readonly ['left', 'center', 'right', 'justify'];
|
||||
};
|
||||
};
|
||||
|
||||
type UploadLoaderBlockConfig = BlockConfig<
|
||||
@@ -90,9 +95,9 @@ const UploadLoaderBlockComponent = ({
|
||||
url: `${mediaUrl}${response.file}`,
|
||||
showPreview: block.props.blockUploadShowPreview,
|
||||
name: block.props.blockUploadName,
|
||||
caption: '',
|
||||
caption: block.props.blockUploadCaption,
|
||||
backgroundColor: 'default',
|
||||
textAlignment: 'left',
|
||||
textAlignment: block.props.blockUploadTextAlignment,
|
||||
},
|
||||
} as never,
|
||||
],
|
||||
@@ -160,6 +165,11 @@ export const UploadLoaderBlock = createReactBlockSpec(
|
||||
default: '',
|
||||
},
|
||||
blockUploadUrl: { default: '' },
|
||||
blockUploadCaption: { default: '' },
|
||||
blockUploadTextAlignment: {
|
||||
default: 'left',
|
||||
values: ['left', 'center', 'right', 'justify'],
|
||||
},
|
||||
},
|
||||
content: 'none',
|
||||
},
|
||||
|
||||
+107
@@ -0,0 +1,107 @@
|
||||
import { render, waitFor } from '@testing-library/react';
|
||||
import { ComponentType } from 'react';
|
||||
import { beforeEach, describe, expect, it, vi } from 'vitest';
|
||||
|
||||
import { AppWrapper } from '@/tests/utils';
|
||||
|
||||
import { UploadLoaderBlock } from '../UploadLoaderBlock';
|
||||
|
||||
const { loopCheckDocMediaStatus, capturedRender } = vi.hoisted(() => ({
|
||||
loopCheckDocMediaStatus: vi.fn(),
|
||||
capturedRender: {
|
||||
current: undefined as ComponentType<Record<string, unknown>> | undefined,
|
||||
},
|
||||
}));
|
||||
|
||||
vi.mock('../../../api', () => ({
|
||||
loopCheckDocMediaStatus,
|
||||
}));
|
||||
|
||||
vi.mock('@blocknote/react', async () => {
|
||||
const actual =
|
||||
await vi.importActual<typeof import('@blocknote/react')>(
|
||||
'@blocknote/react',
|
||||
);
|
||||
|
||||
return {
|
||||
...actual,
|
||||
createReactBlockSpec: (config: unknown, implementation: object) => {
|
||||
if (
|
||||
'render' in implementation &&
|
||||
typeof implementation.render === 'function'
|
||||
) {
|
||||
capturedRender.current = implementation.render as ComponentType<
|
||||
Record<string, unknown>
|
||||
>;
|
||||
}
|
||||
|
||||
return (
|
||||
actual.createReactBlockSpec as (
|
||||
blockConfig: unknown,
|
||||
blockImplementation: object,
|
||||
) => ReturnType<typeof actual.createReactBlockSpec>
|
||||
)(config, implementation);
|
||||
},
|
||||
};
|
||||
});
|
||||
|
||||
void UploadLoaderBlock;
|
||||
|
||||
const analyzeUrl = 'https://docs.example/media-check/abc';
|
||||
|
||||
describe('UploadLoaderBlock', () => {
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks();
|
||||
vi.spyOn(Math, 'random').mockReturnValue(0);
|
||||
loopCheckDocMediaStatus.mockResolvedValue({
|
||||
status: 'ready',
|
||||
file: '/media/doc/photo.png',
|
||||
});
|
||||
});
|
||||
|
||||
it('restores the caption and alignment once the analysis is done', async () => {
|
||||
const replaceBlocks = vi.fn();
|
||||
const Render = capturedRender.current!;
|
||||
|
||||
render(
|
||||
<Render
|
||||
block={{
|
||||
id: 'loader',
|
||||
type: 'uploadLoader',
|
||||
props: {
|
||||
information: 'Analyzing file...',
|
||||
type: 'loading',
|
||||
blockUploadName: 'photo.png',
|
||||
blockUploadShowPreview: true,
|
||||
blockUploadType: 'image',
|
||||
blockUploadUrl: analyzeUrl,
|
||||
blockUploadCaption: 'Pont Neuf',
|
||||
blockUploadTextAlignment: 'center',
|
||||
},
|
||||
content: undefined,
|
||||
children: [],
|
||||
}}
|
||||
editor={{
|
||||
isEditable: true,
|
||||
replaceBlocks,
|
||||
}}
|
||||
/>,
|
||||
{ wrapper: AppWrapper },
|
||||
);
|
||||
|
||||
await waitFor(() => {
|
||||
expect(replaceBlocks).toHaveBeenCalledWith(
|
||||
['loader'],
|
||||
[
|
||||
expect.objectContaining({
|
||||
type: 'image',
|
||||
props: expect.objectContaining({
|
||||
caption: 'Pont Neuf',
|
||||
textAlignment: 'center',
|
||||
}),
|
||||
}),
|
||||
],
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
+40
-1
@@ -4,7 +4,8 @@ import { beforeEach, describe, expect, it, vi } from 'vitest';
|
||||
|
||||
import { AppWrapper } from '@/tests/utils';
|
||||
|
||||
import { useUploadFile } from '../useUploadFile';
|
||||
import { DocsBlockNoteEditor } from '../../types';
|
||||
import { useUploadFile, useUploadStatus } from '../useUploadFile';
|
||||
|
||||
const { mockToast } = vi.hoisted(() => ({ mockToast: vi.fn() }));
|
||||
|
||||
@@ -71,3 +72,41 @@ describe('useUploadFile', () => {
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe('useUploadStatus', () => {
|
||||
it('keeps the caption and alignment while the new file is analyzed', () => {
|
||||
const replaceBlocks = vi.fn();
|
||||
const editor = {
|
||||
document: [
|
||||
{
|
||||
id: 'img',
|
||||
type: 'image',
|
||||
props: {
|
||||
url: 'https://docs.example/media-check/abc',
|
||||
name: 'photo.png',
|
||||
showPreview: true,
|
||||
caption: 'Pont Neuf',
|
||||
textAlignment: 'center',
|
||||
},
|
||||
},
|
||||
],
|
||||
replaceBlocks,
|
||||
onUploadEnd: vi.fn(),
|
||||
} as unknown as DocsBlockNoteEditor;
|
||||
|
||||
renderHook(() => useUploadStatus(editor), { wrapper: AppWrapper });
|
||||
|
||||
expect(replaceBlocks).toHaveBeenCalledWith(
|
||||
['img'],
|
||||
[
|
||||
expect.objectContaining({
|
||||
type: 'uploadLoader',
|
||||
props: expect.objectContaining({
|
||||
blockUploadCaption: 'Pont Neuf',
|
||||
blockUploadTextAlignment: 'center',
|
||||
}),
|
||||
}),
|
||||
],
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -16,6 +16,30 @@ import { DocsBlockNoteEditor } from '../types';
|
||||
|
||||
const DEFAULT_MAX_FILE_SIZE = 10 * 1024 * 1024; // Default to 10MB
|
||||
|
||||
const TEXT_ALIGNMENTS = ['left', 'center', 'right', 'justify'] as const;
|
||||
|
||||
type TextAlignment = (typeof TEXT_ALIGNMENTS)[number];
|
||||
|
||||
const fileCaption = (props: object) => {
|
||||
if ('caption' in props && typeof props.caption === 'string') {
|
||||
return props.caption;
|
||||
}
|
||||
|
||||
return '';
|
||||
};
|
||||
|
||||
const fileTextAlignment = (props: object): TextAlignment => {
|
||||
if (
|
||||
'textAlignment' in props &&
|
||||
typeof props.textAlignment === 'string' &&
|
||||
TEXT_ALIGNMENTS.includes(props.textAlignment as TextAlignment)
|
||||
) {
|
||||
return props.textAlignment as TextAlignment;
|
||||
}
|
||||
|
||||
return 'left';
|
||||
};
|
||||
|
||||
export const useUploadFile = (docId: string) => {
|
||||
const { t } = useTranslation();
|
||||
const { toast } = useToast();
|
||||
@@ -98,6 +122,8 @@ export const useUploadStatus = (editor: DocsBlockNoteEditor) => {
|
||||
const blockUploadName = block.props.name;
|
||||
const blockUploadShowPreview =
|
||||
('showPreview' in block.props && block.props.showPreview) || false;
|
||||
const blockUploadCaption = fileCaption(block.props);
|
||||
const blockUploadTextAlignment = fileTextAlignment(block.props);
|
||||
|
||||
try {
|
||||
editor.replaceBlocks(
|
||||
@@ -112,6 +138,8 @@ export const useUploadStatus = (editor: DocsBlockNoteEditor) => {
|
||||
blockUploadType,
|
||||
blockUploadUrl,
|
||||
blockUploadShowPreview,
|
||||
blockUploadCaption,
|
||||
blockUploadTextAlignment,
|
||||
},
|
||||
},
|
||||
],
|
||||
|
||||
@@ -674,6 +674,8 @@ describe('Conversion Testing', () => {
|
||||
information: 'uploading',
|
||||
type: 'loading' as const,
|
||||
blockUploadName: 'doc.pdf',
|
||||
blockUploadCaption: 'Pont Neuf',
|
||||
blockUploadTextAlignment: 'center' as const,
|
||||
},
|
||||
},
|
||||
];
|
||||
@@ -696,6 +698,8 @@ describe('Conversion Testing', () => {
|
||||
information: 'uploading',
|
||||
type: 'loading',
|
||||
blockUploadName: 'doc.pdf',
|
||||
blockUploadCaption: 'Pont Neuf',
|
||||
blockUploadTextAlignment: 'center',
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@@ -10,6 +10,11 @@ const uploadLoaderPropSchema = {
|
||||
blockUploadShowPreview: { default: true as boolean },
|
||||
blockUploadType: { default: '' as string },
|
||||
blockUploadUrl: { default: '' as string },
|
||||
blockUploadCaption: { default: '' as string },
|
||||
blockUploadTextAlignment: {
|
||||
default: 'left' as const,
|
||||
values: ['left', 'center', 'right', 'justify'] as const,
|
||||
},
|
||||
} as const;
|
||||
|
||||
const uploadLoaderConfig = {
|
||||
|
||||
Reference in New Issue
Block a user