Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion src/app-components/Button/Button.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
import React, { forwardRef } from 'react';
import type { PropsWithChildren } from 'react';

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

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

export type ButtonVariant = 'primary' | 'secondary' | 'tertiary' | undefined;
Expand Down
3 changes: 2 additions & 1 deletion src/app-components/Table/Table.tsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
import React from 'react';
import type { ReactElement } from 'react';

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

import { Spinner } from 'src/app-components/loading/Spinner/Spinner';
import classes from 'src/app-components/Table/Table.module.css';
import utilClasses from 'src/styles/utils.module.css';
import type { FormDataValue } from 'src/app-components/DynamicForm/DynamicForm';
Expand Down
16 changes: 16 additions & 0 deletions src/app-components/error/FatalError/FatalError.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import React from 'react';
import type { HTMLAttributes, PropsWithChildren } from 'react';

/**
* The `data-fatal-error` signals that some unrecoverable error occured which should prevent PDF generation from happening as it would not include necessary information.
*/
export function FatalError({ children, ...props }: PropsWithChildren<HTMLAttributes<HTMLDivElement>>) {
return (
<div
data-fatal-error
{...props}
>
{children}
</div>
);
}
13 changes: 13 additions & 0 deletions src/app-components/error/FatalErrorEmpty/FatalErrorEmpty.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import React from 'react';

/**
* The `data-fatal-error` signals that some unrecoverable error occured which should prevent PDF generation from happening as it would not include necessary information.
*/
export function FatalErrorEmpty() {
return (
<div
data-fatal-error
style={{ display: 'none' }}
/>
);
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ export function AltinnContentIconFormData() {
return (
<>
<rect
data-testid='AltinnContentIconFormData'
x='0'
y='0'
rx='0'
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ export function AltinnContentIconReceipt() {
return (
<>
<rect
data-testid='AltinnContentIconReceipt'
x='12'
y='11'
rx='0'
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import React from 'react';

import { render as rtlRender, screen } from '@testing-library/react';

import {
AltinnContentLoader,
IAltinnContentLoaderProps,
} from 'src/app-components/loading/AltinnContentLoader/AltinnContentLoader';

const render = (props: Omit<IAltinnContentLoaderProps, 'reason'> = {}) => {
const allProps = {
...props,
};

rtlRender(
<AltinnContentLoader
reason='testing'
{...allProps}
/>,
);
};

describe('AltinnContentLoader', () => {
it('should show default loader when no variant is set', () => {
render();

expect(screen.getByTestId('AltinnContentIcon')).toBeInTheDocument();
expect(screen.queryByTestId('AltinnContentIconFormData')).not.toBeInTheDocument();
expect(screen.queryByTestId('AltinnContentIconReceipt')).not.toBeInTheDocument();
});

it('should show form loader when variant=form', () => {
render({ variant: 'form' });

expect(screen.queryByTestId('AltinnContentIconFormData')).toBeInTheDocument();
expect(screen.queryByTestId('AltinnContentIconReceipt')).not.toBeInTheDocument();
expect(screen.queryByTestId('AltinnContentIcon')).not.toBeInTheDocument();
});

it('should show receipt loader when variant=receipt', () => {
render({ variant: 'receipt' });

expect(screen.queryByTestId('AltinnContentIconReceipt')).toBeInTheDocument();
expect(screen.queryByTestId('AltinnContentIconFormData')).not.toBeInTheDocument();
expect(screen.queryByTestId('AltinnContentIcon')).not.toBeInTheDocument();
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
import React from 'react';
import ContentLoader from 'react-content-loader';

import { AltinnContentIcon } from 'src/app-components/loading/AltinnContentLoader/AltinnContentIcon';
import { AltinnContentIconFormData } from 'src/app-components/loading/AltinnContentLoader/AltinnContentIconFormData';
import { AltinnContentIconReceipt } from 'src/app-components/loading/AltinnContentLoader/AltinnContentIconReceipt';

type LoaderVariant = 'default' | 'form' | 'receipt';

export interface IAltinnContentLoaderProps {
reason: string;
details?: string;

variant?: LoaderVariant;
height?: number | string;
width?: number | string;
}

interface LoaderIconProps {
variant?: LoaderVariant;
}

function LoaderIcon({ variant }: LoaderIconProps) {
switch (variant) {
case 'form':
return <AltinnContentIconFormData />;
case 'receipt':
return <AltinnContentIconReceipt />;
case 'default':
default:
return <AltinnContentIcon />;
}
}

/**
* The `data-loading` signals that something is pending and we should not print PDF yet.
*/
export const AltinnContentLoader = ({
reason,
details,
variant,
width = 400,
height = 200,
}: IAltinnContentLoaderProps) => (
<div
data-loading
data-testid='loader'
data-reason={reason}
data-details={details}
>
<ContentLoader
height={height}
width={width}
>
<LoaderIcon variant={variant} />
</ContentLoader>
</div>
);
13 changes: 13 additions & 0 deletions src/app-components/loading/LoadingEmpty/LoadingEmpty.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import React from 'react';

/**
* The `data-loading` signals that something is pending and we should not print PDF yet.
*/
export function LoadingEmpty() {
return (
<div
data-loading
style={{ display: 'none' }}
/>
);
}
16 changes: 16 additions & 0 deletions src/app-components/loading/LoadingWrapper/LoadingWrapper.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import React from 'react';
import type { HTMLAttributes, PropsWithChildren } from 'react';

/**
* The `data-loading` signals that something is pending and we should not print PDF yet.
*/
export function LoadingWrapper({ children, ...props }: PropsWithChildren<HTMLAttributes<HTMLDivElement>>) {
return (
<div
data-loading
{...props}
>
{children}
</div>
);
}
15 changes: 15 additions & 0 deletions src/app-components/loading/Spinner/Spinner.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import React from 'react';

import { Spinner as DesignSystemSpinner } from '@digdir/designsystemet-react';

/**
* The `data-loading` signals that something is pending and we should not print PDF yet.
*/
export function Spinner(props: Parameters<typeof DesignSystemSpinner>[0]) {
return (
<DesignSystemSpinner
data-loading
{...props}
/>
);
}
3 changes: 2 additions & 1 deletion src/components/AltinnSpinner.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
import React from 'react';

import { Paragraph, Spinner } from '@digdir/designsystemet-react';
import { Paragraph } from '@digdir/designsystemet-react';
import classNames from 'classnames';
import type { ArgumentArray } from 'classnames';

import { Spinner } from 'src/app-components/loading/Spinner/Spinner';
import classes from 'src/components/AltinnSpinner.module.css';
import { useLanguage } from 'src/features/language/useLanguage';

Expand Down
3 changes: 2 additions & 1 deletion src/components/PDFGeneratorPreview/PDFGeneratorPreview.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
import React from 'react';

import { Dialog, Heading, Spinner } from '@digdir/designsystemet-react';
import { Dialog, Heading } from '@digdir/designsystemet-react';
import { FilePdfIcon } from '@navikt/aksel-icons';

import { Button } from 'src/app-components/Button/Button';
import { Spinner } from 'src/app-components/loading/Spinner/Spinner';
import classes from 'src/features/devtools/components/PDFPreviewButton/PDFPreview.module.css';
import { useLaxInstanceId } from 'src/features/instance/InstanceContext';
import { useCurrentLanguage } from 'src/features/language/LanguageProvider';
Expand Down
28 changes: 10 additions & 18 deletions src/components/ReadyForPrint.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@ import { useIsFetching } from '@tanstack/react-query';

import { waitForAnimationFrames } from 'src/utils/waitForAnimationFrames';

export const loadingClassName = 'loading';
export const loadingAttribute = 'data-loading';
const errorAttribute = 'data-fatal-error';

type ReadyType = 'print' | 'load';
const readyId: Record<ReadyType, string> = {
Expand All @@ -24,7 +25,8 @@ export function ReadyForPrint({ type }: { type: ReadyType }) {

const isFetching = useIsFetching() > 0;

const hasLoaders = useHasElementsByClass(loadingClassName);
const hasLoaders = useHasElementsByAttribute(loadingAttribute);
const hasErrors = useHasElementsByAttribute(errorAttribute);

React.useLayoutEffect(() => {
if (assetsLoaded) {
Expand All @@ -39,7 +41,7 @@ export function ReadyForPrint({ type }: { type: ReadyType }) {
});
}, [assetsLoaded]);

if (!assetsLoaded || hasLoaders || isFetching || isPending) {
if (!assetsLoaded || hasLoaders || isFetching || isPending || (type === 'print' && hasErrors)) {
return null;
}

Expand Down Expand Up @@ -74,13 +76,12 @@ async function waitForImages() {
} while (nodes.some((node) => !node.complete));
}

export function useHasElementsByClass(className: string) {
const [hasElements, setHasElements] = useState(() => document.getElementsByClassName(className).length > 0);
export function useHasElementsByAttribute(attribute: string) {
const [hasElements, setHasElements] = useState(() => document.querySelector(`[${attribute}]`) != null);

useEffect(() => {
const updateCount = () => {
const newCount = document.getElementsByClassName(className).length;
setHasElements(newCount > 0);
setHasElements(document.querySelector(`[${attribute}]`) != null);
};

const observer = new MutationObserver(updateCount);
Expand All @@ -89,24 +90,15 @@ export function useHasElementsByClass(className: string) {
childList: true,
subtree: true,
attributes: true,
attributeFilter: ['class'],
attributeFilter: [attribute],
});

updateCount();

return () => {
observer.disconnect();
};
}, [className]);
}, [attribute]);

return hasElements;
}

export function BlockPrint() {
return (
<div
className={loadingClassName}
style={{ display: 'none' }}
/>
);
}
5 changes: 3 additions & 2 deletions src/components/altinnError.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import React from 'react';

import cn from 'classnames';

import { FatalError } from 'src/app-components/error/FatalError/FatalError';
import classes from 'src/components/altinnError.module.css';
import { Lang } from 'src/features/language/Lang';
import { altinnAppsIllustrationHelpCircleSvgUrl } from 'src/utils/urls/urlHelper';
Expand Down Expand Up @@ -29,7 +30,7 @@ export const AltinnError = ({
imageAlt,
imageUrl,
}: IAltinnErrorProps) => (
<div
<FatalError
data-testid='AltinnError'
className={classes.flexContainer}
>
Expand Down Expand Up @@ -75,5 +76,5 @@ export const AltinnError = ({
src={imageUrl || altinnAppsIllustrationHelpCircleSvgUrl}
/>
</div>
</div>
</FatalError>
);
33 changes: 0 additions & 33 deletions src/components/molecules/AltinnContentLoader.test.tsx

This file was deleted.

Loading