Skip to content

Commit

Permalink
Merge pull request #729 from open-formulieren/feature/4320-cosign-imp…
Browse files Browse the repository at this point in the history
…rovements

Cosign improvements
  • Loading branch information
sergei-maertens authored Nov 18, 2024
2 parents d70b5ae + c0098ef commit 5ca3c4b
Show file tree
Hide file tree
Showing 23 changed files with 438 additions and 151 deletions.
1 change: 1 addition & 0 deletions src/Context.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ const FormContext = React.createContext({
introductionPageContent: '',
loginRequired: false,
loginOptions: [],
cosignLoginOptions: [],
maintenanceMode: false,
showProgressIndicator: true,
showSummaryProgress: false,
Expand Down
28 changes: 10 additions & 18 deletions src/components/Button/OFButton.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -16,33 +16,25 @@ aria attribute instead.
More information on the Utrecht buttons can be found
[here](https://nl-design-system.github.io/themes/?path=/docs/button--gemeente-utrecht).

<Canvas>
<Story of={OFButtonStories.UtrechtDefault} />
<Story of={OFButtonStories.UtrechtPrimary} />
<Story of={OFButtonStories.UtrechtSecondary} />
<Story of={OFButtonStories.UtrechtDanger} />
</Canvas>
<Canvas of={OFButtonStories.UtrechtDefault} />
<Canvas of={OFButtonStories.UtrechtPrimary} />
<Canvas of={OFButtonStories.UtrechtSecondary} />
<Canvas of={OFButtonStories.UtrechtDanger} />

### Links that look like buttons

<Canvas>
<Story of={OFButtonStories.UtrechtLinkLooksLikeDefaultButton} />
<Story of={OFButtonStories.UtrechtLinkLooksLikePrimaryButton} />
<Story of={OFButtonStories.UtrechtLinkLooksLikeSecondaryButton} />
</Canvas>
<Canvas of={OFButtonStories.UtrechtLinkLooksLikeDefaultButton} />
<Canvas of={OFButtonStories.UtrechtLinkLooksLikePrimaryButton} />
<Canvas of={OFButtonStories.UtrechtLinkLooksLikeSecondaryButton} />

### Button that looks like a link

<Canvas>
<Story of={OFButtonStories.UtrechtButtonLooksLikeLink} />
</Canvas>
<Canvas of={OFButtonStories.UtrechtButtonLooksLikeLink} />

## Icon buttons

<Canvas>
<Story of={OFButtonStories.UtrechtIconButton} />
<Story of={OFButtonStories.UtrechtIconButtonDanger} />
</Canvas>
<Canvas of={OFButtonStories.UtrechtIconButton} />
<Canvas of={OFButtonStories.UtrechtIconButtonDanger} />

## Disabled state

Expand Down
4 changes: 2 additions & 2 deletions src/components/CoSign/CoSign.stories.jsx
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
import CosignDone from './CosignDone';

export default {
title: 'Views / Co-sign done',
title: 'Views / Cosign / Done',
component: CosignDone,
args: {
reportDownloadUrl: '#',
},
};

export const CoSignDone = {
name: 'Co-sign done',
name: 'CosignDone',
};
2 changes: 2 additions & 0 deletions src/components/CoSign/Cosign.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import {CosignSummary} from 'components/Summary';
import useFormContext from 'hooks/useFormContext';

import CosignDone from './CosignDone';
import CosignStart from './CosignStart';

const initialState = {
submission: null,
Expand Down Expand Up @@ -60,6 +61,7 @@ const Cosign = () => {
return (
<ErrorBoundary useCard>
<Routes>
<Route path="start" element={<CosignStart />} />
<Route
path="check"
element={
Expand Down
82 changes: 82 additions & 0 deletions src/components/CoSign/CosignStart.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
import {FormattedMessage} from 'react-intl';

import Body from 'components/Body';
import Card from 'components/Card';
import {LiteralsProvider} from 'components/Literal';
import LoginOptions from 'components/LoginOptions';
import MaintenanceMode from 'components/MaintenanceMode';
import {
AuthenticationErrors,
useDetectAuthErrorMessages,
} from 'components/auth/AuthenticationErrors';
import AuthenticationOutage, {
useDetectAuthenticationOutage,
} from 'components/auth/AuthenticationOutage';
import {UnprocessableEntity} from 'errors';
import {IsFormDesigner} from 'headers';
import useFormContext from 'hooks/useFormContext';

const CosignStart = () => {
const form = useFormContext();

const outagePluginId = useDetectAuthenticationOutage();
const authErrors = useDetectAuthErrorMessages();

if (!form.active) {
throw new UnprocessableEntity('Unprocessable Entity', 422, 'Form not active', 'form-inactive');
}

const userIsFormDesigner = IsFormDesigner.getValue();
if (!userIsFormDesigner && form.maintenanceMode) {
return <MaintenanceMode title={form.name} />;
}

if (outagePluginId) {
const loginOption = form.cosignLoginOptions.find(
option => option.identifier === outagePluginId
);
if (!loginOption) throw new Error('Unknown login plugin identifier');
return (
<Card
title={
<FormattedMessage
description="Form start outage title"
defaultMessage="Problem - {formName}"
values={{formName: form.name}}
/>
}
>
<AuthenticationOutage loginOption={loginOption} />
</Card>
);
}

return (
<LiteralsProvider literals={form.literals}>
<Card title={form.name}>
{userIsFormDesigner && form.maintenanceMode && <MaintenanceMode asToast />}

{!!authErrors ? <AuthenticationErrors parameters={authErrors} /> : null}

<Body>
<FormattedMessage
description="Cosign start explanation message"
defaultMessage={`Did you receive an email with a request to cosign?
Start the cosigning by logging in.`}
/>
</Body>

<LoginOptions
// hide the normal login options, and only display the cosign login options
form={{...form, loginOptions: []}}
// dummy - we don't actually start a new submission, but the next URL is baked
// into the login URLs.
onFormStart={() => {}}
isolateCosignOptions={false}
/>
</Card>
</LiteralsProvider>
);
};

export default CosignStart;
51 changes: 51 additions & 0 deletions src/components/CoSign/CosignStart.stories.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
import {withRouter} from 'storybook-addon-remix-react-router';

import {buildForm} from 'api-mocks';
import {withForm} from 'story-utils/decorators';

import CosignStart from './CosignStart';

export default {
title: 'Views / Cosign / Start',
component: CosignStart,
decorators: [withForm, withRouter],
parameters: {
formContext: {
form: buildForm({
loginRequired: true,
loginOptions: [
{
identifier: 'digid',
label: 'DigiD',
url: '#',
logo: {
title: 'DigiD simulatie',
imageSrc: './digid.png',
href: 'https://www.digid.nl/',
appearance: 'dark',
},
isForGemachtigde: false,
},
],
cosignLoginOptions: [
{
identifier: 'digid',
label: 'DigiD',
url: 'http://localhost:8000/auth/digid/?next=http://localhost:8000/cosign&amp;code=123',
logo: {
title: 'DigiD simulatie',
imageSrc: './digid.png',
href: 'https://www.digid.nl/',
appearance: 'dark',
},
isForGemachtigde: false,
},
],
}),
},
},
};

export const Default = {
name: 'CosignStart',
};
8 changes: 7 additions & 1 deletion src/components/FormStart/index.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -125,7 +125,13 @@ const FormStart = ({form, submission, onFormStart, onDestroySession, initialData
isAuthenticated={isAuthenticated}
/>
) : (
<LoginOptions form={form} onFormStart={onFormStart} extraNextParams={extraNextParams} />
<LoginOptions
// if cosign allows links in emails, we don't need to display the cosign
// login options, so strip them out
form={form.cosignHasLinkInEmail ? {...form, cosignLoginOptions: []} : form}
onFormStart={onFormStart}
extraNextParams={extraNextParams}
/>
)}
</Card>
</LiteralsProvider>
Expand Down
8 changes: 6 additions & 2 deletions src/components/LoginOptions/LoginOptions.stories.jsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import {expect, fn, userEvent, waitFor, within} from '@storybook/test';
import {withRouter} from 'storybook-addon-remix-react-router';

import {buildForm} from 'api-mocks';
import {LiteralDecorator} from 'story-utils/decorators';
Expand All @@ -9,7 +10,10 @@ import LoginOptionsDisplay from './LoginOptionsDisplay';
export default {
title: 'Composites / Login Options',
component: LoginOptions,
decorators: [LiteralDecorator],
decorators: [LiteralDecorator, withRouter],
args: {
onFormStart: fn(),
},
argTypes: {
form: {table: {disable: true}},
},
Expand Down Expand Up @@ -239,7 +243,7 @@ export const WithCoSignOption = {
{
identifier: 'digid',
label: 'DigiD',
url: '#',
url: 'http://localhost:8000/auth/digid/?next=http://localhost:3000/form?_start=1',
logo: {
title: 'DigiD simulatie',
imageSrc: './digid.png',
Expand Down
30 changes: 23 additions & 7 deletions src/components/LoginOptions/LoginOptionsDisplay.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import PropTypes from 'prop-types';
import React from 'react';
import {FormattedMessage} from 'react-intl';

import Body from 'components/Body';
import LoginButton from 'components/LoginButton';
import FormattedLoginOption from 'types/FormattedLoginOption';
import {getBEMClassName} from 'utils';
Expand All @@ -10,6 +11,7 @@ const LoginOptionsDisplay = ({
loginAsYourselfOptions,
loginAsGemachtigdeOptions,
cosignLoginOptions,
isolateCosignOptions = true,
}) => {
return (
<div className={getBEMClassName('login-options')}>
Expand Down Expand Up @@ -37,13 +39,26 @@ const LoginOptionsDisplay = ({
)}

{cosignLoginOptions?.length > 0 && (
<div className={getBEMClassName('login-options__cosign')}>
<h2 className={getBEMClassName('login-options__caption')}>
<FormattedMessage
description="Log in to co-sign the form title"
defaultMessage="Log in to co-sign the form"
/>
</h2>
<div
className={isolateCosignOptions ? getBEMClassName('login-options__cosign') : undefined}
>
{isolateCosignOptions && (
<>
<h2 className={getBEMClassName('login-options__caption')}>
<FormattedMessage
description="Log in to co-sign the form title"
defaultMessage="Log in to co-sign the form"
/>
</h2>
<Body>
<FormattedMessage
description="Cosign start explanation message"
defaultMessage={`Did you receive an email with a request to cosign?
Start the cosigning by logging in.`}
/>
</Body>
</>
)}

<div className={getBEMClassName('login-options__list')}>
{cosignLoginOptions.map(option => (
Expand All @@ -60,6 +75,7 @@ LoginOptionsDisplay.propTypes = {
loginAsYourselfOptions: PropTypes.arrayOf(FormattedLoginOption).isRequired,
loginAsGemachtigdeOptions: PropTypes.arrayOf(FormattedLoginOption).isRequired,
cosignLoginOptions: PropTypes.arrayOf(FormattedLoginOption),
isolateCosignOptions: PropTypes.bool,
};

export default LoginOptionsDisplay;
12 changes: 10 additions & 2 deletions src/components/LoginOptions/index.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,15 @@ import {FormattedMessage} from 'react-intl';

import {ConfigContext} from 'Context';
import Literal from 'components/Literal';
import {getLoginUrl} from 'components/utils';
import {getCosignLoginUrl, getLoginUrl} from 'components/utils';
import useQuery from 'hooks/useQuery';
import Types from 'types';

import LoginOptionsDisplay from './LoginOptionsDisplay';

const LoginOptions = ({form, onFormStart, extraNextParams = {}}) => {
const LoginOptions = ({form, onFormStart, extraNextParams = {}, isolateCosignOptions = true}) => {
const config = useContext(ConfigContext);
const queryParams = useQuery();

const loginAsYourselfOptions = [];
const loginAsGemachtigdeOptions = [];
Expand All @@ -35,9 +37,12 @@ const LoginOptions = ({form, onFormStart, extraNextParams = {}}) => {
});

if (form.cosignLoginOptions) {
const cosignCode = queryParams.get('code');
form.cosignLoginOptions.forEach(option => {
const loginUrl = getCosignLoginUrl(option, cosignCode ? {code: cosignCode} : undefined);
cosignLoginOptions.push({
...option,
url: loginUrl,
label: (
<FormattedMessage
description="Login button label"
Expand Down Expand Up @@ -65,6 +70,7 @@ const LoginOptions = ({form, onFormStart, extraNextParams = {}}) => {
e.preventDefault();
onFormStart(e, true);
},
'data-testid': 'start-form',
};

return (
Expand All @@ -73,6 +79,7 @@ const LoginOptions = ({form, onFormStart, extraNextParams = {}}) => {
loginAsYourselfOptions={loginAsYourselfOptions}
loginAsGemachtigdeOptions={loginAsGemachtigdeOptions}
cosignLoginOptions={cosignLoginOptions}
isolateCosignOptions={isolateCosignOptions}
/>
</Container>
);
Expand All @@ -83,6 +90,7 @@ LoginOptions.propTypes = {
onFormStart: PropTypes.func.isRequired,
extraParams: PropTypes.object,
extraNextParams: PropTypes.object,
isolateCosignOptions: PropTypes.bool,
};

export default LoginOptions;
Loading

0 comments on commit 5ca3c4b

Please sign in to comment.