2
0
mirror of https://github.com/inventree/InvenTree.git synced 2025-05-02 13:28:49 +00:00

Template Editor Improvements (#9434)

* add unmount hook and context to report editor ui feature

* Fix tests

* Update pui_printing.spec.ts

* Update pui_printing.spec.ts

* try fix tests

* retrigger ci

* remove debug messages
This commit is contained in:
Lukas 2025-04-02 00:01:12 +02:00 committed by GitHub
parent 6a964a4e2b
commit ba703cf58a
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
10 changed files with 30 additions and 22 deletions

View File

@ -175,7 +175,7 @@ class SampleUserInterfacePlugin(SettingsMixin, UserInterfaceMixin, InvenTreePlug
{ {
'key': 'sample-template-editor', 'key': 'sample-template-editor',
'title': 'Sample Template Editor', 'title': 'Sample Template Editor',
'icon': 'keywords', 'icon': 'ti:tags:outline',
'source': self.plugin_static_file( 'source': self.plugin_static_file(
'sample_template.js:getTemplateEditor' 'sample_template.js:getTemplateEditor'
), ),
@ -190,7 +190,7 @@ class SampleUserInterfacePlugin(SettingsMixin, UserInterfaceMixin, InvenTreePlug
{ {
'key': 'sample-template-preview', 'key': 'sample-template-preview',
'title': 'Sample Template Preview', 'title': 'Sample Template Preview',
'icon': 'category', 'icon': 'ti:category:outline',
'source': self.plugin_static_file( 'source': self.plugin_static_file(
'sample_preview.js:getTemplatePreview' 'sample_preview.js:getTemplatePreview'
), ),

View File

@ -4,7 +4,7 @@ export function getTemplatePreview({ featureContext, pluginContext }) {
featureContext.registerHandlers({ featureContext.registerHandlers({
updatePreview: (...args) => { updatePreview: (...args) => {
console.log("updatePreview", args); console.log("updatePreview", args[0]);
} }
}); });

View File

@ -7,6 +7,6 @@ import { CodeEditorComponent } from './CodeEditor';
export const CodeEditor: Editor = { export const CodeEditor: Editor = {
key: 'code', key: 'code',
name: t`Code`, name: t`Code`,
icon: IconCode, icon: <IconCode size={18} />,
component: CodeEditorComponent component: CodeEditorComponent
}; };

View File

@ -7,6 +7,6 @@ import { PdfPreviewComponent } from './PdfPreview';
export const PdfPreview: PreviewArea = { export const PdfPreview: PreviewArea = {
key: 'pdf-preview', key: 'pdf-preview',
name: t`PDF Preview`, name: t`PDF Preview`,
icon: IconFileTypePdf, icon: <IconFileTypePdf size={18} />,
component: PdfPreviewComponent component: PdfPreviewComponent
}; };

View File

@ -26,7 +26,6 @@ import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
import { api } from '../../../App'; import { api } from '../../../App';
import { ModelType } from '../../../enums/ModelType'; import { ModelType } from '../../../enums/ModelType';
import type { TablerIconType } from '../../../functions/icons';
import { apiUrl } from '../../../states/ApiState'; import { apiUrl } from '../../../states/ApiState';
import type { TemplateI } from '../../../tables/settings/TemplateTable'; import type { TemplateI } from '../../../tables/settings/TemplateTable';
import { Boundary } from '../../Boundary'; import { Boundary } from '../../Boundary';
@ -50,7 +49,7 @@ export type EditorComponent = React.ForwardRefExoticComponent<
export type Editor = { export type Editor = {
key: string; key: string;
name: string; name: string;
icon?: TablerIconType; icon?: React.ReactNode;
component: EditorComponent; component: EditorComponent;
}; };
@ -72,7 +71,7 @@ export type PreviewAreaComponent = React.ForwardRefExoticComponent<
export type PreviewArea = { export type PreviewArea = {
key: string; key: string;
name: string; name: string;
icon: TablerIconType; icon: React.ReactNode;
component: PreviewAreaComponent; component: PreviewAreaComponent;
}; };
@ -272,7 +271,7 @@ export function TemplateEditor(props: Readonly<TemplateEditorProps>) {
<Tabs.Tab <Tabs.Tab
key={Editor.key} key={Editor.key}
value={Editor.key} value={Editor.key}
leftSection={Editor.icon && <Editor.icon size='0.8rem' />} leftSection={Editor.icon}
> >
{Editor.name} {Editor.name}
</Tabs.Tab> </Tabs.Tab>
@ -336,9 +335,7 @@ export function TemplateEditor(props: Readonly<TemplateEditorProps>) {
<Tabs.Tab <Tabs.Tab
key={PreviewArea.key} key={PreviewArea.key}
value={PreviewArea.key} value={PreviewArea.key}
leftSection={ leftSection={PreviewArea.icon}
PreviewArea.icon && <PreviewArea.icon size='0.8rem' />
}
> >
{PreviewArea.name} {PreviewArea.name}
</Tabs.Tab> </Tabs.Tab>

View File

@ -82,9 +82,10 @@ export const getPluginTemplateEditor = (
})); }));
useEffect(() => { useEffect(() => {
let unmountHandler: (() => void) | undefined;
(async () => { (async () => {
try { try {
func({ unmountHandler = await func({
ref: elRef.current!, ref: elRef.current!,
registerHandlers: ({ getCode, setCode }) => { registerHandlers: ({ getCode, setCode }) => {
setCodeRef.current = setCode; setCodeRef.current = setCode;
@ -101,6 +102,12 @@ export const getPluginTemplateEditor = (
console.error(error); console.error(error);
} }
})(); })();
return () => {
if (typeof unmountHandler === 'function') {
unmountHandler();
}
};
}, []); }, []);
return ( return (

View File

@ -14,9 +14,13 @@ export type BaseUIFeature = {
featureReturnType: any; featureReturnType: any;
}; };
export type PluginUIGetFeatureType<T extends BaseUIFeature> = (params: { export type PluginUIGetFeatureType<
T extends BaseUIFeature,
ServerContext extends Record<string, unknown>
> = (params: {
featureContext: T['featureContext']; featureContext: T['featureContext'];
inventreeContext: InvenTreeContext; inventreeContext: InvenTreeContext;
serverContext: ServerContext;
}) => T['featureReturnType']; }) => T['featureReturnType'];
export type PluginUIFuncWithoutInvenTreeContextType<T extends BaseUIFeature> = ( export type PluginUIFuncWithoutInvenTreeContextType<T extends BaseUIFeature> = (
@ -25,9 +29,8 @@ export type PluginUIFuncWithoutInvenTreeContextType<T extends BaseUIFeature> = (
export type PluginUIFeatureAPIResponse<T extends BaseUIFeature> = { export type PluginUIFeatureAPIResponse<T extends BaseUIFeature> = {
feature_type: T['featureType']; feature_type: T['featureType'];
options: T['responseOptions'];
source: string; source: string;
}; } & T['responseOptions'];
// #region Types // #region Types
export type TemplateEditorUIFeature = { export type TemplateEditorUIFeature = {
@ -45,7 +48,7 @@ export type TemplateEditorUIFeature = {
}) => void; }) => void;
template: TemplateI; template: TemplateI;
}; };
featureReturnType: undefined; featureReturnType: (() => void) | undefined;
}; };
export type TemplatePreviewUIFeature = { export type TemplatePreviewUIFeature = {

View File

@ -84,7 +84,8 @@ export function usePluginUIFeature<UIFeatureT extends BaseUIFeature>({
return func({ return func({
featureContext, featureContext,
inventreeContext inventreeContext,
serverContext: feature.context
}); });
}) as PluginUIFuncWithoutInvenTreeContextType<UIFeatureT> }) as PluginUIFuncWithoutInvenTreeContextType<UIFeatureT>
}; };

View File

@ -16,6 +16,7 @@ import type {
PreviewArea PreviewArea
} from '../../components/editors/TemplateEditor/TemplateEditor'; } from '../../components/editors/TemplateEditor/TemplateEditor';
import type { ApiFormFieldSet } from '../../components/forms/fields/ApiFormField'; import type { ApiFormFieldSet } from '../../components/forms/fields/ApiFormField';
import { ApiIcon } from '../../components/items/ApiIcon';
import { AttachmentLink } from '../../components/items/AttachmentLink'; import { AttachmentLink } from '../../components/items/AttachmentLink';
import { DetailDrawer } from '../../components/nav/DetailDrawer'; import { DetailDrawer } from '../../components/nav/DetailDrawer';
import { import {
@ -29,7 +30,6 @@ import type {
import type { ApiEndpoints } from '../../enums/ApiEndpoints'; import type { ApiEndpoints } from '../../enums/ApiEndpoints';
import type { ModelType } from '../../enums/ModelType'; import type { ModelType } from '../../enums/ModelType';
import { identifierString } from '../../functions/conversion'; import { identifierString } from '../../functions/conversion';
import { GetIcon } from '../../functions/icons';
import { notYetImplemented } from '../../functions/notifications'; import { notYetImplemented } from '../../functions/notifications';
import { useFilters } from '../../hooks/UseFilter'; import { useFilters } from '../../hooks/UseFilter';
import { import {
@ -115,7 +115,7 @@ export function TemplateDrawer({
`${editor.options.plugin_name}-${editor.options.key}` `${editor.options.plugin_name}-${editor.options.key}`
), ),
name: editor.options.title, name: editor.options.title,
icon: GetIcon(editor.options.icon || 'plugin'), icon: <ApiIcon name={editor.options.icon || 'plugin'} size={18} />,
component: getPluginTemplateEditor(editor.func, template) component: getPluginTemplateEditor(editor.func, template)
} as Editor; } as Editor;
}) || []) }) || [])
@ -143,7 +143,7 @@ export function TemplateDrawer({
({ ({
key: preview.options.key, key: preview.options.key,
name: preview.options.title, name: preview.options.title,
icon: GetIcon(preview.options.icon || 'plugin'), icon: <ApiIcon name={preview.options.icon || 'plugin'} size={18} />,
component: getPluginTemplatePreview(preview.func, template) component: getPluginTemplatePreview(preview.func, template)
}) as PreviewArea }) as PreviewArea
) || []) ) || [])

View File

@ -138,7 +138,7 @@ test('Report Editing', async ({ browser, request }) => {
.click(); .click();
const msg = (await consoleLogPromise).args(); const msg = (await consoleLogPromise).args();
expect(await msg[0].jsonValue()).toBe('updatePreview'); expect(await msg[0].jsonValue()).toBe('updatePreview');
expect((await msg[1].jsonValue())[0]).toBe(newTextareaValue); expect(await msg[1].jsonValue()).toBe(newTextareaValue);
// deactivate the sample plugin again after the test // deactivate the sample plugin again after the test
await setPluginState({ await setPluginState({