Skip to content

Commit

Permalink
Feature/11500 app lib version (#11673)
Browse files Browse the repository at this point in the history
* implementing app-lib-version

* adding todo

* adding alert text

* refactor to new file

* refactoring canvas to separate files and adding tests

* fix typecheck

* fixing feedback from PR
  • Loading branch information
WilliamThorenfeldt authored Nov 28, 2023
1 parent 5a9c9d4 commit 81111b8
Show file tree
Hide file tree
Showing 37 changed files with 630 additions and 140 deletions.
21 changes: 19 additions & 2 deletions frontend/app-development/features/processEditor/ProcessEditor.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,18 @@ import { useBpmnQuery } from 'app-development/hooks/queries/useBpmnQuery';
import React from 'react';
import { useStudioUrlParams } from 'app-shared/hooks/useStudioUrlParams';
import { toast } from 'react-toastify';
import { useAppLibVersionQuery } from 'app-development/hooks/queries';
import { Spinner } from '@digdir/design-system-react';
import { useTranslation } from 'react-i18next';

export const ProcessEditor = () => {
const { t } = useTranslation();

const { org, app } = useStudioUrlParams();
const { data: bpmnXml, isError: hasBpmnQueryError } = useBpmnQuery(org, app);

const { data: appLibData, isLoading: appLibDataLoading } = useAppLibVersionQuery(org, app);

const bpmnMutation = useBpmnMutation(org, app);

const saveBpmnXml = async (xml: string): Promise<void> => {
Expand All @@ -18,10 +25,20 @@ export const ProcessEditor = () => {
onSuccess: () => {
toast.success('Bpmn saved successfully');
},
}
},
);
};

if (appLibDataLoading) {
return <Spinner title={t('process_editor.loading')} />;
}

// TODO: Handle error will be handled better after issue #10735 is resolved
return <ProcessEditorImpl bpmnXml={hasBpmnQueryError ? null : bpmnXml} onSave={saveBpmnXml} />;
return (
<ProcessEditorImpl
bpmnXml={hasBpmnQueryError ? null : bpmnXml}
onSave={saveBpmnXml}
appLibVersion={appLibData.version}
/>
);
};
1 change: 1 addition & 0 deletions frontend/app-development/hooks/queries/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,3 +11,4 @@ export { useUserQuery } from 'app-shared/hooks/queries/useUserQuery';
export { useAppPolicyQuery } from './useAppPolicyQuery';
export { useAppConfigQuery } from './useAppConfigQuery';
export { useAppMetadataQuery } from './useAppMetadataQuery';
export { useAppLibVersionQuery } from './useAppLibVersionQuery';
18 changes: 18 additions & 0 deletions frontend/app-development/hooks/queries/useAppLibVersionQuery.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { useQuery } from '@tanstack/react-query';
import { getAppLibVersion } from 'app-shared/api/queries';
import { QueryKey } from 'app-shared/types/QueryKey';

/**
* Query to get the app-lib version.
*
* @param org the organisation of the user
* @param repo the repo the user is in
*
* @returns UseQueryResult with the version
*/
export const useAppLibVersionQuery = (org: string, repo: string) => {
return useQuery<{ version: string }>({
queryKey: [QueryKey.AppLibVersion, org, repo],
queryFn: () => getAppLibVersion(org, repo),
});
};
4 changes: 4 additions & 0 deletions frontend/language/src/nb.json
Original file line number Diff line number Diff line change
Expand Up @@ -649,12 +649,16 @@
"process_editor.edit_mode": "Redigeringsmodus",
"process_editor.fetch_bpmn_error_message": "Kunne ikke laste inn BPMN for denne appen. Prøv igjen senere.",
"process_editor.fetch_bpmn_error_title": "En feil oppstod ved innlasting av BPMN",
"process_editor.help_text_and_links": "<0>Les mer om hvordan dette gjøres her</0>. Trenger du hjelp til å oppgradere, kan du <1>kontakte oss</1>.",
"process_editor.loading": "Laster inn BPMN",
"process_editor.need_to_contact_text": "Du kan se på prosessen, men for å redigere den i dette verktøyet må du oppgradere til versjon 8 eller nyere.",
"process_editor.not_found_diagram_error_message": "Vi kan dessverre ikke vise BPMN-diagrammet for øyeblikket. En mulig årsak til dette kan være at BPMN-filen din ikke inneholder et diagram som er riktig definert innenfor bpmn:diagram-taggen. Du kan verifisere om diagram-taggen finnes i BPMN-filen. En løsning kan være å rekonstruere diagrammet på nytt ved hjelp av prosessverktøyet.",
"process_editor.not_found_diagram_heading": "Ingen tilgjengelig diagramdata",
"process_editor.not_found_process_error_message": "Det finnes ingen prosess definert innenfor BPMN, Du kan verifisere om prosessen finnes i BPMN-filen.",
"process_editor.not_found_process_heading": "Ingen tilgjengelig prosess",
"process_editor.save": "Lagre",
"process_editor.too_old_version_text": "Applikasjonen din har versjon {{ version }} av app-lib-pakken.\nVi støtter kun redigering av prosesser for applikasjoner som bruker versjon 8 eller nyere.",
"process_editor.too_old_version_title": "Du kan ikke redigere denne prosessen",
"process_editor.unknown_heading_error_message": "Obs, noe gikk galt!",
"process_editor.unknown_paragraph_error_message": "En feil oppstod ved innlasting av BPMN-prosessen. Undersøk at filen er på et gyldig BPMN-format.",
"process_editor.unsaved_changes": "Du har {{ count }} ulagrede endringer",
Expand Down
62 changes: 53 additions & 9 deletions frontend/packages/process-editor/src/ProcessEditor.test.tsx
Original file line number Diff line number Diff line change
@@ -1,32 +1,76 @@
import React from 'react';
import { render, screen, act } from '@testing-library/react';
import { ProcessEditor } from './ProcessEditor';
import { render, screen, act } from '@testing-library/react';
import { ProcessEditor, ProcessEditorProps } from './ProcessEditor';
import { textMock } from '../../../testing/mocks/i18nMock';
import userEvent from '@testing-library/user-event';

const mockBPMNXML: string = `<?xml version="1.0" encoding="UTF-8"?></xml>`;

const mockAppLibVersion8: string = '8.0.3';
const mockAppLibVersion7: string = '7.0.3';

const mockOnSave = jest.fn();

describe('ProcessEditor', () => {
afterEach(jest.clearAllMocks);

const defaultProps: ProcessEditorProps = {
bpmnXml: mockBPMNXML,
onSave: mockOnSave,
appLibVersion: mockAppLibVersion8,
};

it('should render loading while bpmnXml is undefined', () => {
render(<ProcessEditor bpmnXml={undefined} onSave={() => {}} />);
render(<ProcessEditor {...defaultProps} bpmnXml={undefined} />);
expect(screen.getByTitle(textMock('process_editor.loading'))).toBeInTheDocument();
});

it('should render "NoBpmnFoundAlert" when bpmnXml is null', () => {
render(<ProcessEditor bpmnXml={null} onSave={() => {}} />);
render(<ProcessEditor {...defaultProps} bpmnXml={null} />);
expect(
screen.getByRole('heading', {
name: textMock('process_editor.fetch_bpmn_error_title'),
level: 2,
})
}),
).toBeInTheDocument();
});

it('should render "canvas" when bpmnXml is provided and default render is view-mode', async () => {
// eslint-disable-next-line testing-library/no-unnecessary-act
await act(() => {
render(<ProcessEditor bpmnXml={`<?xml version="1.0" encoding="UTF-8"?></xml>`} onSave={() => { } } />);
})
await act(() => {
render(<ProcessEditor {...defaultProps} />);
});

expect(
screen.getByRole('button', { name: textMock('process_editor.edit_mode') })
screen.getByRole('button', { name: textMock('process_editor.edit_mode') }),
).toBeInTheDocument();
});

it('does not display the alert when the version is 8 or newer', async () => {
const user = userEvent.setup();
render(<ProcessEditor {...defaultProps} />);

// Fix to remove act error
await act(() => user.tab());

const alertHeader = screen.queryByRole('heading', {
name: textMock('process_editor.too_old_version_title'),
level: 1,
});
expect(alertHeader).not.toBeInTheDocument();
});

it('displays the alert when the version is 7 or older', async () => {
const user = userEvent.setup();
render(<ProcessEditor {...defaultProps} appLibVersion={mockAppLibVersion7} />);

// Fix to remove act error
await act(() => user.tab());

const alertHeader = screen.getByRole('heading', {
name: textMock('process_editor.too_old_version_title'),
level: 1,
});
expect(alertHeader).toBeInTheDocument();
});
});
13 changes: 10 additions & 3 deletions frontend/packages/process-editor/src/ProcessEditor.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,19 @@ import { PageLoading } from './components/PageLoading';
import { Canvas } from './components/Canvas';
import { BpmnContextProvider } from './contexts/BpmnContext';

type ProcessEditorProps = {
export type ProcessEditorProps = {
bpmnXml: string | undefined | null;
onSave: (bpmnXml: string) => void;
appLibVersion: string;
};
export const ProcessEditor = ({ bpmnXml, onSave }: ProcessEditorProps): JSX.Element => {

export const ProcessEditor = ({
bpmnXml,
onSave,
appLibVersion,
}: ProcessEditorProps): JSX.Element => {
const { t } = useTranslation();

if (bpmnXml === undefined) {
return <PageLoading title={t('process_editor.loading')} />;
}
Expand All @@ -21,7 +28,7 @@ export const ProcessEditor = ({ bpmnXml, onSave }: ProcessEditorProps): JSX.Elem

return (
<BpmnContextProvider bpmnXml={bpmnXml}>
<Canvas onSave={onSave} />
<Canvas onSave={onSave} appLibVersion={appLibVersion} />
</BpmnContextProvider>
);
};
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
.editorContainer {
display: flex;
flex: 1;
border: 1px solid var(--fds-semantic-border-neutral-default);
margin-inline: 18px;
margin-bottom: 18px;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import React, { ReactNode } from 'react';
import classes from './BPMNEditor.module.css';
import { useBpmnEditor } from '../../../hooks/useBpmnEditor';

/**
* @component
* Displays the editor canvas in the ProcessEditor
*
* @returns {ReactNode} - The rendered component
*/
export const BPMNEditor = (): ReactNode => {
const { canvasRef } = useBpmnEditor();
return <div className={classes.editorContainer} ref={canvasRef}></div>;
};
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { BPMNEditor } from './BPMNEditor';
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
.viewerWrapper {
width: 100%;
display: flex;
gap: 1rem;
}

.canvasContainer {
display: flex;
gap: 1rem;
width: 100%;
border: 1px solid var(--fds-semantic-border-neutral-default);
margin-inline: 18px;
margin-bottom: 18px;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import React from 'react';
import { render, screen, act } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import { BPMNViewer, BPMNViewerProps } from './BPMNViewer';
import { textMock } from '../../../../../../testing/mocks/i18nMock';

const mockAppLibVersion8: string = '8.0.1';
const mockAppLibVersion7: string = '7.0.1';

const defaultProps: BPMNViewerProps = {
appLibVersion: mockAppLibVersion8,
};

describe('Viewer', () => {
afterEach(jest.clearAllMocks);

it('displays version alert when version is 7 or older', async () => {
const user = userEvent.setup();
render(<BPMNViewer {...defaultProps} appLibVersion={mockAppLibVersion7} />);

// Fix to remove act error
await act(() => user.tab());

const alertTitle = screen.getByRole('heading', {
name: textMock('process_editor.too_old_version_title'),
level: 1,
});
expect(alertTitle).toBeInTheDocument;
});

it('hides version alert when version is 8 or newer', async () => {
const user = userEvent.setup();
render(<BPMNViewer {...defaultProps} />);

// Fix to remove act error
await act(() => user.tab());

const alertTitle = screen.queryByRole('heading', {
name: textMock('process_editor.too_old_version_title'),
level: 1,
});
expect(alertTitle).not.toBeInTheDocument;
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import React, { ReactNode } from 'react';
import classes from './BPMNViewer.module.css';
import { useBpmnViewer } from '../../../hooks/useBpmnViewer';
import { VersionAlert } from './VersionAlert';
import { BPMNViewerErrorAlert } from './BPMNViewerErrorAlert';
import { supportsProcessEditor } from '../../../utils/processEditorUtils';

export type BPMNViewerProps = {
appLibVersion: string;
};

/**
* @component
* Displays the canvas area of the ProcessEditor
*
* @property {string}[appLibVersion] - The app-lib version the user has
*
* @returns {ReactNode} - The rendered component
*/
export const BPMNViewer = ({ appLibVersion }: BPMNViewerProps): ReactNode => {
const { canvasRef, bpmnViewerError } = useBpmnViewer();

const isEditAllowed: boolean = supportsProcessEditor(appLibVersion);

return (
<>
{bpmnViewerError !== undefined && <BPMNViewerErrorAlert bpmnViewerError={bpmnViewerError} />}
<div className={classes.viewerWrapper}>
<div className={classes.canvasContainer} ref={canvasRef}></div>
{!isEditAllowed && <VersionAlert appLibVersion={appLibVersion} />}
</div>
</>
);
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
.alertContainer {
padding: 18px;
box-sizing: border-box;
max-width: 780px;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
import React from 'react';
import { render, screen, act } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import { BPMNViewerErrorAlert } from './BPMNViewerErrorAlert';
import { textMock } from '../../../../../../../testing/mocks/i18nMock';

describe('Viewer', () => {
afterEach(jest.clearAllMocks);

it('displays correct error message when bpmnViewerError is "noDiagram"', async () => {
const user = userEvent.setup();
render(<BPMNViewerErrorAlert bpmnViewerError='noDiagram' />);

// Fix to remove act error
await act(() => user.tab());

const heading = screen.getByRole('heading', {
name: textMock('process_editor.not_found_diagram_heading'),
});
const paragraph = screen.getByText(textMock('process_editor.not_found_diagram_error_message'));

expect(heading).toBeInTheDocument();
expect(paragraph).toBeInTheDocument();
});

it('displays correct error message when bpmnViewerError is "noProcess"', async () => {
const user = userEvent.setup();
render(<BPMNViewerErrorAlert bpmnViewerError='noProcess' />);

// Fix to remove act error
await act(() => user.tab());

const heading = screen.getByRole('heading', {
name: textMock('process_editor.not_found_process_heading'),
});
const paragraph = screen.getByText(textMock('process_editor.not_found_process_error_message'));

expect(heading).toBeInTheDocument();
expect(paragraph).toBeInTheDocument();
});

it('displays correct error message when bpmnViewerError is "unknown"', async () => {
const user = userEvent.setup();
render(<BPMNViewerErrorAlert bpmnViewerError='unknown' />);

// Fix to remove act error
await act(() => user.tab());

const heading = screen.getByRole('heading', {
name: textMock('process_editor.unknown_heading_error_message'),
});
const paragraph = screen.getByText(textMock('process_editor.unknown_paragraph_error_message'));

expect(heading).toBeInTheDocument();
expect(paragraph).toBeInTheDocument();
});
});
Loading

0 comments on commit 81111b8

Please sign in to comment.