Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feat/1804 pdf preview #2710

Merged
merged 10 commits into from
Nov 18, 2024
Original file line number Diff line number Diff line change
Expand Up @@ -4,27 +4,37 @@ import { Button, Modal, Spinner } from '@digdir/designsystemet-react';
import { FilePdfIcon } from '@navikt/aksel-icons';

import classes from 'src/features/devtools/components/PDFPreviewButton/PDFPreview.module.css';
import { useLaxInstanceId } from 'src/features/instance/InstanceContext';
import { useLaxInstance } from 'src/features/instance/InstanceContext';
import { useTaskTypeFromBackend } from 'src/features/instance/ProcessContext';
import { Lang } from 'src/features/language/Lang';
import { useCurrentLanguage } from 'src/features/language/LanguageProvider';
import { useLanguage } from 'src/features/language/useLanguage';
import { useIsLocalOrStaging } from 'src/hooks/useIsDev';
import { ProcessTaskType } from 'src/types';
import { getPdfPreviewUrl } from 'src/utils/urls/appUrlHelper';

export function PDFGeneratorPreview() {
export function PDFGeneratorPreview({
buttonTitle,
showErrorDetails,
}: {
buttonTitle?: string;
showErrorDetails?: boolean;
}) {
const modalRef = React.useRef<HTMLDialogElement>(null);
const abortRef = React.useRef<AbortController | null>(null);

const [blobUrl, setBlobUrl] = React.useState<string | null>(null);
const [errorText, setErrorText] = React.useState<string | null>(null);

const taskType = useTaskTypeFromBackend();
const instanceId = useLaxInstanceId();
const instanceId = useLaxInstance((state) => state.instanceId);
const language = useCurrentLanguage();
const isLocalOrStaging = useIsLocalOrStaging();

const disabled = taskType !== ProcessTaskType.Data || !instanceId || !isLocalOrStaging;

const { langAsString } = useLanguage();

async function generatePDF() {
if (disabled) {
return;
Expand Down Expand Up @@ -69,7 +79,7 @@ export function PDFGeneratorPreview() {
aria-hidden
/>
}
Generer PDF
{buttonTitle ? langAsString(buttonTitle) : langAsString('pdfPreview.defaultButtonText')}
</Button>
<Modal
ref={modalRef}
Expand All @@ -84,21 +94,24 @@ export function PDFGeneratorPreview() {
src={blobUrl}
/>
) : errorText ? (
<>
<Modal.Header>PDF-generering feilet</Modal.Header>
<div style={{ textAlign: 'center' }}>
<Modal.Header>
<Lang id={'pdfPreview.error'} />
</Modal.Header>
<Modal.Content>
{errorText.split('\n').map((line) => (
<>
{line}
<br />
</>
))}
{showErrorDetails &&
errorText.split('\n').map((line) => (
<>
{line}
<br />
</>
))}
</Modal.Content>
</>
</div>
) : (
<div className={classes.loading}>
<Spinner
title='Laster...'
title={langAsString('general.loading')}
size='xlarge'
/>
</div>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,19 +3,19 @@ import React from 'react';
import { Button, Fieldset } from '@digdir/designsystemet-react';
import { FilePdfIcon } from '@navikt/aksel-icons';

import { PDFGeneratorPreview } from 'src/features/devtools/components/PDFPreviewButton/PDFGeneratorPreview';
import { PDFGeneratorPreview } from 'src/components/PDFGeneratorPreview/PDFGeneratorPreview';
import classes from 'src/features/devtools/components/PDFPreviewButton/PDFPreview.module.css';
import { useDevToolsStore } from 'src/features/devtools/data/DevToolsStore';
import { useTaskTypeFromBackend } from 'src/features/instance/ProcessContext';
import { useIsLocalOrStaging } from 'src/hooks/useIsDev';
import { useIsStudioPreview } from 'src/hooks/useIsDev';
import { ProcessTaskType } from 'src/types';

export const PDFPreviewButton = () => {
const taskType = useTaskTypeFromBackend();
const setPdfPreview = useDevToolsStore((state) => state.actions.setPdfPreview);

// PDF generator is not available in altinn studio preview, and the preview API is disabled in production
const isLocalOrStaging = useIsLocalOrStaging();
// PDF generator is not available in altinn studio preview
const isStudioPreview = useIsStudioPreview();

return (
<Fieldset
Expand Down Expand Up @@ -45,7 +45,12 @@ export const PDFPreviewButton = () => {
}
Forhåndsvis PDF
</Button>
{isLocalOrStaging && <PDFGeneratorPreview />}
{!isStudioPreview && (
<PDFGeneratorPreview
showErrorDetails={true}
buttonTitle={'Generer PDF'}
/>
)}
</Fieldset>
);
};
2 changes: 1 addition & 1 deletion src/features/instance/InstanceContext.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -216,7 +216,7 @@ const BlockUntilLoaded = ({ children }: PropsWithChildren) => {
* know should only be used in instanceful contexts. Code paths that have to work in stateless/instanceless contexts
* should use the lax versions and handle the undefined case.
*/
function useLaxInstance<U>(selector: (state: InstanceContext) => U) {
export function useLaxInstance<U>(selector: (state: InstanceContext) => U) {
const out = useLaxMemoSelector(selector);
return out === ContextNotProvided ? undefined : out;
}
Expand Down
4 changes: 4 additions & 0 deletions src/language/texts/en.ts
Original file line number Diff line number Diff line change
Expand Up @@ -413,5 +413,9 @@ export function en() {
process_error: {
submit_error_please_retry: 'Something went wrong when submitting, please try again in a few minutes.',
},
pdfPreview: {
error: 'Could not show PDF preview',
defaultButtonText: 'Preview PDF',
},
} satisfies NestedTexts;
}
4 changes: 4 additions & 0 deletions src/language/texts/nb.ts
Original file line number Diff line number Diff line change
Expand Up @@ -414,5 +414,9 @@ export function nb(): FixedLanguageList {
process_error: {
submit_error_please_retry: 'Noe gikk galt under innsendingen, prøv igjen om noen minutter.',
},
pdfPreview: {
error: 'Kunne ikke forhåndsvise PDF',
defaultButtonText: 'Forhåndsvis PDF',
},
} satisfies NestedTexts;
}
4 changes: 4 additions & 0 deletions src/language/texts/nn.ts
Original file line number Diff line number Diff line change
Expand Up @@ -414,5 +414,9 @@ export function nn(): FixedLanguageList {
process_error: {
submit_error_please_retry: 'Noko gjekk gale med innsending, prøv igjen om nokre minutt.',
},
pdfPreview: {
error: 'Kunne ikkje førehandsvise PDF',
defaultButtonText: 'Førehandsvis PDF',
},
} satisfies NestedTexts;
}
29 changes: 29 additions & 0 deletions src/layout/PDFPreviewButton/PDFPreviewButtonComponent.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import React from 'react';

import type { PropsFromGenericComponent } from '..';

import { PDFGeneratorPreview } from 'src/components/PDFGeneratorPreview/PDFGeneratorPreview';
import { useStrictInstanceId } from 'src/features/instance/InstanceContext';
import { NodesInternal } from 'src/utils/layout/NodesContext';
import { useNodeItem } from 'src/utils/layout/useNodeItem';
import type { NodeValidationProps } from 'src/layout/layout';

export type IActionButton = PropsFromGenericComponent<'PDFPreviewButton'>;

export function PDFPreviewButtonRenderLayoutValidator({ node }: NodeValidationProps<'PDFPreviewButton'>) {
const instanceId = useStrictInstanceId();

const addError = NodesInternal.useAddError();

if (!instanceId) {
const error = `Cannot use PDF preview button in a stateless app`;
addError(error, node);
window.logErrorOnce(`Validation error for '${node.id}': ${error}`);
}
return null;
}
bjosttveit marked this conversation as resolved.
Show resolved Hide resolved

export function PDFPreviewButtonComponent({ node }: IActionButton) {
const { textResourceBindings } = useNodeItem(node);
return <PDFGeneratorPreview buttonTitle={textResourceBindings?.title} />;
}
34 changes: 34 additions & 0 deletions src/layout/PDFPreviewButton/config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import { CG } from 'src/codegen/CG';
import { CompCategory } from 'src/layout/common';

export const Config = new CG.component({
category: CompCategory.Action,
capabilities: {
renderInTable: true,
renderInButtonGroup: true,
renderInAccordion: false,
renderInAccordionGroup: false,
renderInCards: true,
renderInCardsMedia: false,
renderInTabs: true,
},
functionality: {
customExpressions: false,
},
})
.addTextResource(
new CG.trb({
name: 'title',
title: 'Button title/text',
description: 'The text to display on the button.',
}),
)
.addProperty(
new CG.prop(
'buttonStyle',
new CG.enum('primary', 'secondary')
.setTitle('Button style')
.setDescription('The style/color scheme of the button.')
.exportAs('ActionButtonStyle'),
),
);
22 changes: 22 additions & 0 deletions src/layout/PDFPreviewButton/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import React, { forwardRef } from 'react';
import type { JSX } from 'react';

import { PDFPreviewButtonDef } from 'src/layout/PDFPreviewButton/config.def.generated';
import {
PDFPreviewButtonComponent,
PDFPreviewButtonRenderLayoutValidator,
} from 'src/layout/PDFPreviewButton/PDFPreviewButtonComponent';
import type { PropsFromGenericComponent } from 'src/layout';
import type { NodeValidationProps } from 'src/layout/layout';

export class PDFPreviewButton extends PDFPreviewButtonDef {
render = forwardRef<HTMLElement, PropsFromGenericComponent<'PDFPreviewButton'>>(
function LayoutComponentActionButtonRender(props, _): JSX.Element | null {
return <PDFPreviewButtonComponent {...props} />;
},
);

renderLayoutValidators(props: NodeValidationProps<'PDFPreviewButton'>): JSX.Element | null {
return <PDFPreviewButtonRenderLayoutValidator {...props} />;
}
}
Loading