Skip to content

Commit 2517d53

Browse files
authored
Improve loading and error handling in ReadyForPrint (#3840)
* improve loading and error handling in readyforprint * fix test description * strict equality * use fatal error in dev as well * use loading attribute in signeelistsummary * add todo * clarify comments * remove comment
1 parent 7942dc0 commit 2517d53

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

44 files changed

+375
-172
lines changed

src/app-components/Button/Button.tsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
11
import React, { forwardRef } from 'react';
22
import type { PropsWithChildren } from 'react';
33

4-
import { Button as DesignSystemButton, Spinner } from '@digdir/designsystemet-react';
4+
import { Button as DesignSystemButton } from '@digdir/designsystemet-react';
55
import type { ButtonProps as DesignSystemButtonProps } from '@digdir/designsystemet-react';
66

7+
import { Spinner } from 'src/app-components/loading/Spinner/Spinner';
78
import { useLanguage } from 'src/features/language/useLanguage';
89

910
export type ButtonVariant = 'primary' | 'secondary' | 'tertiary' | undefined;

src/app-components/Table/Table.tsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,13 @@
11
import React from 'react';
22
import type { ReactElement } from 'react';
33

4-
import { Button, Spinner, Table } from '@digdir/designsystemet-react';
4+
import { Button, Table } from '@digdir/designsystemet-react';
55
import cn from 'classnames';
66
import { format, isValid, parseISO } from 'date-fns';
77
import { pick } from 'dot-object';
88
import type { JSONSchema7 } from 'json-schema';
99

10+
import { Spinner } from 'src/app-components/loading/Spinner/Spinner';
1011
import classes from 'src/app-components/Table/Table.module.css';
1112
import utilClasses from 'src/styles/utils.module.css';
1213
import type { FormDataValue } from 'src/app-components/DynamicForm/DynamicForm';
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
import React from 'react';
2+
import type { HTMLAttributes, PropsWithChildren } from 'react';
3+
4+
/**
5+
* The `data-fatal-error` signals that some unrecoverable error occured which should prevent PDF generation from happening as it would not include necessary information.
6+
*/
7+
export function FatalError({ children, ...props }: PropsWithChildren<HTMLAttributes<HTMLDivElement>>) {
8+
return (
9+
<div
10+
data-fatal-error
11+
{...props}
12+
>
13+
{children}
14+
</div>
15+
);
16+
}
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
import React from 'react';
2+
3+
/**
4+
* The `data-fatal-error` signals that some unrecoverable error occured which should prevent PDF generation from happening as it would not include necessary information.
5+
*/
6+
export function FatalErrorEmpty() {
7+
return (
8+
<div
9+
data-fatal-error
10+
style={{ display: 'none' }}
11+
/>
12+
);
13+
}

src/components/atoms/AltinnContentIconFormData.tsx renamed to src/app-components/loading/AltinnContentLoader/AltinnContentIconFormData.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ export function AltinnContentIconFormData() {
44
return (
55
<>
66
<rect
7+
data-testid='AltinnContentIconFormData'
78
x='0'
89
y='0'
910
rx='0'

src/components/atoms/AltinnContentIconReceipt.tsx renamed to src/app-components/loading/AltinnContentLoader/AltinnContentIconReceipt.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ export function AltinnContentIconReceipt() {
44
return (
55
<>
66
<rect
7+
data-testid='AltinnContentIconReceipt'
78
x='12'
89
y='11'
910
rx='0'
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
import React from 'react';
2+
3+
import { render as rtlRender, screen } from '@testing-library/react';
4+
5+
import {
6+
AltinnContentLoader,
7+
IAltinnContentLoaderProps,
8+
} from 'src/app-components/loading/AltinnContentLoader/AltinnContentLoader';
9+
10+
const render = (props: Omit<IAltinnContentLoaderProps, 'reason'> = {}) => {
11+
const allProps = {
12+
...props,
13+
};
14+
15+
rtlRender(
16+
<AltinnContentLoader
17+
reason='testing'
18+
{...allProps}
19+
/>,
20+
);
21+
};
22+
23+
describe('AltinnContentLoader', () => {
24+
it('should show default loader when no variant is set', () => {
25+
render();
26+
27+
expect(screen.getByTestId('AltinnContentIcon')).toBeInTheDocument();
28+
expect(screen.queryByTestId('AltinnContentIconFormData')).not.toBeInTheDocument();
29+
expect(screen.queryByTestId('AltinnContentIconReceipt')).not.toBeInTheDocument();
30+
});
31+
32+
it('should show form loader when variant=form', () => {
33+
render({ variant: 'form' });
34+
35+
expect(screen.queryByTestId('AltinnContentIconFormData')).toBeInTheDocument();
36+
expect(screen.queryByTestId('AltinnContentIconReceipt')).not.toBeInTheDocument();
37+
expect(screen.queryByTestId('AltinnContentIcon')).not.toBeInTheDocument();
38+
});
39+
40+
it('should show receipt loader when variant=receipt', () => {
41+
render({ variant: 'receipt' });
42+
43+
expect(screen.queryByTestId('AltinnContentIconReceipt')).toBeInTheDocument();
44+
expect(screen.queryByTestId('AltinnContentIconFormData')).not.toBeInTheDocument();
45+
expect(screen.queryByTestId('AltinnContentIcon')).not.toBeInTheDocument();
46+
});
47+
});
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
import React from 'react';
2+
import ContentLoader from 'react-content-loader';
3+
4+
import { AltinnContentIcon } from 'src/app-components/loading/AltinnContentLoader/AltinnContentIcon';
5+
import { AltinnContentIconFormData } from 'src/app-components/loading/AltinnContentLoader/AltinnContentIconFormData';
6+
import { AltinnContentIconReceipt } from 'src/app-components/loading/AltinnContentLoader/AltinnContentIconReceipt';
7+
8+
type LoaderVariant = 'default' | 'form' | 'receipt';
9+
10+
export interface IAltinnContentLoaderProps {
11+
reason: string;
12+
details?: string;
13+
14+
variant?: LoaderVariant;
15+
height?: number | string;
16+
width?: number | string;
17+
}
18+
19+
interface LoaderIconProps {
20+
variant?: LoaderVariant;
21+
}
22+
23+
function LoaderIcon({ variant }: LoaderIconProps) {
24+
switch (variant) {
25+
case 'form':
26+
return <AltinnContentIconFormData />;
27+
case 'receipt':
28+
return <AltinnContentIconReceipt />;
29+
case 'default':
30+
default:
31+
return <AltinnContentIcon />;
32+
}
33+
}
34+
35+
/**
36+
* The `data-loading` signals that something is pending and we should not print PDF yet.
37+
*/
38+
export const AltinnContentLoader = ({
39+
reason,
40+
details,
41+
variant,
42+
width = 400,
43+
height = 200,
44+
}: IAltinnContentLoaderProps) => (
45+
<div
46+
data-loading
47+
data-testid='loader'
48+
data-reason={reason}
49+
data-details={details}
50+
>
51+
<ContentLoader
52+
height={height}
53+
width={width}
54+
>
55+
<LoaderIcon variant={variant} />
56+
</ContentLoader>
57+
</div>
58+
);
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
import React from 'react';
2+
3+
/**
4+
* The `data-loading` signals that something is pending and we should not print PDF yet.
5+
*/
6+
export function LoadingEmpty() {
7+
return (
8+
<div
9+
data-loading
10+
style={{ display: 'none' }}
11+
/>
12+
);
13+
}

0 commit comments

Comments
 (0)