👔(app-impress) save the templates editor automatically
On almost each change in the code editor, the template is saved automatically. It will restore as well the editor when we arrive on the template editor page.
This commit is contained in:
@@ -26,4 +26,37 @@ test.describe('Template Editor', () => {
|
||||
page.locator('.gjs-editor .gjs-block[title="Text"]'),
|
||||
).toBeVisible();
|
||||
});
|
||||
|
||||
test('checks the template editor save on changed', async ({
|
||||
page,
|
||||
browserName,
|
||||
}) => {
|
||||
const randomTemplate = await createTemplate(
|
||||
page,
|
||||
'template-editor',
|
||||
browserName,
|
||||
1,
|
||||
);
|
||||
|
||||
await expect(page.locator('h2').getByText(randomTemplate[0])).toBeVisible();
|
||||
|
||||
const iframe = page.frameLocator('iFrame.gjs-frame');
|
||||
|
||||
await page.getByTitle('Open Blocks').click();
|
||||
await page
|
||||
.locator('.gjs-editor .gjs-block[title="Text"]')
|
||||
.dragTo(iframe.locator('body.gjs-dashed'));
|
||||
|
||||
await iframe.getByText('Insert your text here').fill('Hello World');
|
||||
await iframe.locator('body.gjs-dashed').click();
|
||||
|
||||
// Come on the page again to check the changes are saved
|
||||
await page.locator('menu').first().getByLabel(`Template button`).click();
|
||||
const panel = page.getByLabel('Templates panel').first();
|
||||
await panel.locator('li').getByText(randomTemplate[0]).click();
|
||||
|
||||
await expect(iframe.getByText('Hello World')).toBeVisible({
|
||||
timeout: 5000,
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -0,0 +1,39 @@
|
||||
import { useMutation } from '@tanstack/react-query';
|
||||
|
||||
import { APIError, errorCauses, fetchAPI } from '@/api';
|
||||
|
||||
import { Template } from '../types';
|
||||
|
||||
type UpdateTemplateProps = Pick<Template, 'code_editor' | 'id'>;
|
||||
|
||||
export const updateTemplateCodeEditor = async ({
|
||||
code_editor,
|
||||
id,
|
||||
}: UpdateTemplateProps): Promise<Template> => {
|
||||
const response = await fetchAPI(`templates/${id}/`, {
|
||||
method: 'PATCH',
|
||||
body: JSON.stringify({
|
||||
code_editor,
|
||||
}),
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
throw new APIError(
|
||||
'Failed to update the template',
|
||||
await errorCauses(response),
|
||||
);
|
||||
}
|
||||
|
||||
return response.json() as Promise<Template>;
|
||||
};
|
||||
|
||||
export function useUpdateTemplateCodeEditor(
|
||||
onSuccess?: (data: Template) => void,
|
||||
) {
|
||||
return useMutation<Template, APIError, UpdateTemplateProps>({
|
||||
mutationFn: updateTemplateCodeEditor,
|
||||
onSuccess: (data) => {
|
||||
onSuccess?.(data);
|
||||
},
|
||||
});
|
||||
}
|
||||
@@ -2,6 +2,7 @@ import GjsEditor from '@grapesjs/react';
|
||||
import grapesjs, { Editor, ProjectData } from 'grapesjs';
|
||||
import 'grapesjs/dist/css/grapes.min.css';
|
||||
import pluginBlocksBasic from 'grapesjs-blocks-basic';
|
||||
import { useEffect, useState } from 'react';
|
||||
|
||||
import { Card, Text } from '@/components';
|
||||
|
||||
@@ -13,7 +14,44 @@ interface TemplateEditorProps {
|
||||
}
|
||||
|
||||
export const TemplateEditor = ({ template }: TemplateEditorProps) => {
|
||||
const onEditor = (editor: Editor) => {};
|
||||
const { mutate: updateCodeEditor } = useUpdateTemplateCodeEditor();
|
||||
const [editor, setEditor] = useState<Editor>();
|
||||
|
||||
useEffect(() => {
|
||||
if (!editor?.loadProjectData) {
|
||||
return;
|
||||
}
|
||||
|
||||
editor.loadProjectData(template.code_editor);
|
||||
}, [template.code_editor, editor]);
|
||||
|
||||
useEffect(() => {
|
||||
editor?.Storage.add('remote', {
|
||||
load() {
|
||||
return Promise.resolve(template.code_editor);
|
||||
},
|
||||
store(data: ProjectData) {
|
||||
updateCodeEditor({
|
||||
code_editor: data,
|
||||
id: template.id,
|
||||
});
|
||||
return Promise.resolve();
|
||||
},
|
||||
});
|
||||
}, [editor, template.code_editor, template.id, updateCodeEditor]);
|
||||
|
||||
const onEditor = (editor: Editor) => {
|
||||
setEditor(editor);
|
||||
|
||||
editor?.Storage.add('remote', {
|
||||
load() {
|
||||
return Promise.resolve(template.code_editor);
|
||||
},
|
||||
store() {
|
||||
return Promise.resolve();
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
@@ -24,7 +62,9 @@ export const TemplateEditor = ({ template }: TemplateEditorProps) => {
|
||||
<GjsEditor
|
||||
grapesjs={grapesjs}
|
||||
options={{
|
||||
storageManager: false,
|
||||
storageManager: {
|
||||
type: 'remote',
|
||||
},
|
||||
}}
|
||||
plugins={[(editor) => pluginBlocksBasic(editor, {})]}
|
||||
onEditor={onEditor}
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
import { ProjectData } from 'grapesjs';
|
||||
|
||||
export enum Role {
|
||||
MEMBER = 'member',
|
||||
ADMIN = 'administrator',
|
||||
@@ -27,5 +29,6 @@ export interface Template {
|
||||
update: boolean;
|
||||
};
|
||||
accesses: Access[];
|
||||
code_editor: ProjectData;
|
||||
title: string;
|
||||
}
|
||||
|
||||
@@ -26,7 +26,18 @@ interface TemplateProps {
|
||||
}
|
||||
|
||||
const Template = ({ id }: TemplateProps) => {
|
||||
const { data: template, isLoading, isError, error } = useTemplate({ id });
|
||||
const {
|
||||
data: template,
|
||||
isLoading,
|
||||
isError,
|
||||
error,
|
||||
} = useTemplate(
|
||||
{ id },
|
||||
{
|
||||
queryKey: ['template', { id }],
|
||||
staleTime: 0,
|
||||
},
|
||||
);
|
||||
const navigate = useNavigate();
|
||||
|
||||
if (isError && error) {
|
||||
|
||||
Reference in New Issue
Block a user