Skip to content

Commit

Permalink
Merge branch 'develop' into fix/PN-14012
Browse files Browse the repository at this point in the history
  • Loading branch information
leleOFA authored Feb 28, 2025
2 parents edea16d + f16a9d1 commit a07074a
Show file tree
Hide file tree
Showing 22 changed files with 1,139 additions and 256 deletions.
84 changes: 45 additions & 39 deletions packages/pn-commons/src/components/ConfirmationModal.tsx
Original file line number Diff line number Diff line change
@@ -1,60 +1,66 @@
import * as React from 'react';
import React, { JSXElementConstructor } from 'react';

import { Button, ButtonProps, DialogTitle } from '@mui/material';
import { PnDialog, PnDialogActions, PnDialogContent } from '@pagopa-pn/pn-commons';

type Props = {
open: boolean;
title: string;
slots?: {
confirmButton: JSXElementConstructor<ButtonProps>;
closeButton?: JSXElementConstructor<ButtonProps>;
};
slotsProps?: {
confirmButton?: ButtonProps;
closeButton?: ButtonProps;
};
onConfirmLabel?: string;
onCloseLabel?: string;
children?: React.ReactNode;
};

const ConfirmationModal: React.FC<Props> = ({
open,
title,
slots,
slotsProps,
onConfirmLabel = 'Riprova',
onCloseLabel = 'Annulla',
children,
}: Props) => (
<PnDialog
id="confirmation-dialog"
open={open}
onClose={slotsProps?.closeButton?.onClick}
aria-labelledby="confirmation-dialog-title"
aria-describedby="confirmation-dialog-description"
maxWidth="sm"
data-testid="confirmationDialog"
>
<DialogTitle id="confirmation-dialog-title">{title}</DialogTitle>
{children && <PnDialogContent>{children}</PnDialogContent>}
<PnDialogActions>
<Button
id="dialog-close-button"
color="primary"
variant="outlined"
data-testid="closeButton"
{...slotsProps?.closeButton}
>
{onCloseLabel}
</Button>
<Button
id="dialog-confirm-button"
color="primary"
variant="contained"
data-testid="confirmButton"
{...slotsProps?.confirmButton}
>
{onConfirmLabel}
</Button>
</PnDialogActions>
</PnDialog>
);
}: Props) => {
const ConfirmButton = slots?.confirmButton || Button;
const CloseButton = slots?.closeButton;

return (
<PnDialog
id="confirmation-dialog"
open={open}
onClose={slotsProps?.closeButton?.onClick}
aria-labelledby="confirmation-dialog-title"
aria-describedby="confirmation-dialog-description"
maxWidth="sm"
data-testid="confirmationDialog"
>
<DialogTitle id="confirmation-dialog-title">{title}</DialogTitle>
{children && (
<PnDialogContent id="confirmation-dialog-description">{children}</PnDialogContent>
)}
<PnDialogActions>
{CloseButton && (
<CloseButton
id="dialog-close-button"
color="primary"
variant="outlined"
data-testid="closeButton"
{...slotsProps?.closeButton}
/>
)}
<ConfirmButton
id="dialog-confirm-button"
color="primary"
variant="contained"
data-testid="confirmButton"
{...slotsProps?.confirmButton}
/>
</PnDialogActions>
</PnDialog>
);
};

export default ConfirmationModal;
Original file line number Diff line number Diff line change
@@ -1,23 +1,24 @@
import { vi } from 'vitest';

import { Button } from '@mui/material';

import { fireEvent, render } from '../../test-utils';
import ConfirmationModal from '../ConfirmationModal';

const mockCancelFunction = vi.fn();
const mockCloseFunction = vi.fn();
const mockConfirmFunction = vi.fn();

describe('ConfirmationModal Component', () => {
it('renders the component', () => {
const { getByRole, getByTestId } = render(
<ConfirmationModal
open
title={'Test title'}
slots={{ confirmButton: Button, closeButton: Button }}
slotsProps={{
closeButton: { onClick: mockCancelFunction },
confirmButton: { onClick: mockConfirmFunction },
closeButton: { onClick: mockCloseFunction, children: 'Close' },
confirmButton: { onClick: mockConfirmFunction, children: 'Confirm' },
}}
onCloseLabel={'Cancel'}
open
onConfirmLabel={'Confirm'}
/>
);

Expand All @@ -27,60 +28,57 @@ describe('ConfirmationModal Component', () => {
const confirmButton = getByTestId('confirmButton');
const closeButton = getByTestId('closeButton');
expect(confirmButton).toHaveTextContent(/Confirm/i);
expect(closeButton).toHaveTextContent(/Cancel/i);
expect(closeButton).toHaveTextContent(/Close/i);
});

it('checks that the confirm and cancel functions are executed', () => {
it('checks that the confirm and close functions are executed', () => {
const { getByTestId } = render(
<ConfirmationModal
open
title={'Test title'}
slots={{ confirmButton: Button, closeButton: Button }}
slotsProps={{
closeButton: { onClick: mockCancelFunction },
confirmButton: { onClick: mockConfirmFunction },
closeButton: { onClick: mockCloseFunction, children: 'Close' },
confirmButton: { onClick: mockConfirmFunction, children: 'Confirm' },
}}
onCloseLabel={'Cancel'}
open
onConfirmLabel={'Confirm'}
/>
);

const confirmButton = getByTestId('confirmButton');
const cancelButton = getByTestId('closeButton');
const closeButton = getByTestId('closeButton');
fireEvent.click(confirmButton);
expect(mockConfirmFunction).toHaveBeenCalledTimes(1);
fireEvent.click(cancelButton);
expect(mockCancelFunction).toHaveBeenCalledTimes(1);
fireEvent.click(closeButton);
expect(mockCloseFunction).toHaveBeenCalledTimes(1);
});

it('renders the dialog with default labels', () => {
const { getByTestId } = render(
it('renders the dialog with no close button', () => {
const { getByTestId, queryByTestId } = render(
<ConfirmationModal
open
title={'Test title'}
slots={{ confirmButton: Button }}
slotsProps={{
closeButton: { onClick: mockCancelFunction },
confirmButton: { onClick: mockConfirmFunction },
confirmButton: { onClick: mockConfirmFunction, children: 'Confirm' },
}}
open
/>
);

const confirmButton = getByTestId('confirmButton');
const cancelButton = getByTestId('closeButton');
expect(confirmButton).toHaveTextContent(/Riprova/i);
expect(cancelButton).toHaveTextContent(/Annulla/i);
const closeButton = queryByTestId('closeButton');
expect(confirmButton).toHaveTextContent(/Confirm/i);
expect(closeButton).not.toBeInTheDocument();
});

it('renders the dialog with children', () => {
const { getByRole } = render(
<ConfirmationModal
open
title={'Test title'}
slotsProps={{
closeButton: { onClick: mockCancelFunction },
confirmButton: { onClick: mockConfirmFunction },
closeButton: { onClick: mockCloseFunction, children: 'Close' },
confirmButton: { onClick: mockConfirmFunction, children: 'Confirm' },
}}
onCloseLabel={'Cancel'}
open
onConfirmLabel={'Confirm'}
>
<p>Test Content</p>
</ConfirmationModal>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -210,13 +210,17 @@
"contact-to-add": "Domicilio Digitale",
"contact-to-add-description": "Il domicilio digitale personalizzato dove riceverai le notifiche SEND che ti invia un ente specifico",
"contact-already-exists": "L'ente selezionato è già associato a questo recapito. Se premi “Associa”, verrà sostituito da quello che hai indicato qui.",
"validating-pec": "Esiste già un indirizzo PEC in corso di validazione per l'ente selezionato",
"validating-pec": "Per l’ente selezionato non è possibile associare il domicilio digitale SEND perchè la PEC è in validazione.",
"fetch-party-error": "Non siamo riusciti a recuperare i dati che servono per poter creare un nuovo recapito.",
"add-title": "Inserisci l’ente e il recapito da associare",
"card-title": "PERSONALIZZATI PER ENTE",
"sender": "Ente",
"remove-special-title": "Vuoi eliminare {{contactValue}}?",
"remove-special-description": "Gli avvisi relativi alle notifiche degli enti associati non saranno più inviati a questo recapito, ma a quello principale."
"remove-special-description": "Gli avvisi relativi alle notifiche degli enti associati non saranno più inviati a questo recapito, ma a quello principale.",
"legal-association-title": "Conferma modifica recapito",
"legal-association-description": "Hai già selezionato in precedenza un recapito a valore legale per <strong>{{senderName}}</strong>. Confermi che per le notifiche inviate da <strong>{{senderName}}</strong> il tuo recapito a valore legale sarà <strong>{{newAddress}}</strong> invece che <strong>{{oldAddress}}</strong>.",
"legal-association-title-block": "Recapito già inserito precedentemente",
"legal-association-description-block": "Ti comunichiamo che le notifiche inviate da <strong>{{senderName}}</strong> sono già inoltrate al recapito a valore legale <strong>{{oldAddress}}</strong> inserito in precedenza."
},
"common": {
"duplicate-contact-title": "Recapito già presente",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,10 +46,12 @@ import { isPFEvent } from '../../utility/mixpanel';
import DropDownPartyMenuItem from '../Party/DropDownParty';
import ContactCodeDialog from './ContactCodeDialog';
import ExistingContactDialog from './ExistingContactDialog';
import LegalContactAssociationDialog from './LegalContactAssociationDialog';

enum ModalType {
EXISTING = 'existing',
CODE = 'code',
CONFIRM_LEGAL_ASSOCIATION = 'confirm_legal_association',
}

enum ErrorBannerType {
Expand Down Expand Up @@ -120,9 +122,9 @@ const AddSpecialContact = forwardRef<AddSpecialContactRef, Props>(
(address) => address.senderId === senderId && !address.pecValid
);

const isSenderAlreadyAdded = (sender: Party, channelType: ChannelType | string) =>
const isSenderAlreadyAdded = (sender: Party, channelType?: ChannelType | string) =>
addressesData.specialAddresses.some(
(a) => a.senderId === sender.id && a.channelType === channelType
(a) => a.senderId === sender.id && (!channelType || a.channelType === channelType)
);

const updateErrorBanner = (sender: Party, channelType: ChannelType) => {
Expand Down Expand Up @@ -219,6 +221,11 @@ const AddSpecialContact = forwardRef<AddSpecialContactRef, Props>(
},
});

// verify if the sender already has a contact associated
const oldAddress = addressesData.specialAddresses.find(
(addr) => addr.senderId === formik.values.sender.id
);

const labelRoot = `legal-contacts`;
const contactType = formik.values.channelType.toLowerCase();

Expand Down Expand Up @@ -263,8 +270,16 @@ const AddSpecialContact = forwardRef<AddSpecialContactRef, Props>(
source: ContactSource.RECAPITI,
});
}
// verify if the sender already has a contact associated
const oldAddress = addressesData.specialAddresses.find(
(addr) => addr.senderId === sender.senderId
);
if (oldAddress) {
setModalOpen(ModalType.CONFIRM_LEGAL_ASSOCIATION);
return;
}

// first check if contact already exists
// check if contact already exists
if (contactAlreadyExists(addressesData.addresses, value, sender.senderId, channelType)) {
setModalOpen(ModalType.EXISTING);
return;
Expand Down Expand Up @@ -401,6 +416,21 @@ const AddSpecialContact = forwardRef<AddSpecialContactRef, Props>(

return (
<Paper data-testid="addSpecialContact" sx={{ p: { xs: 2, lg: 3 }, mb: 3 }}>
<LegalContactAssociationDialog
open={modalOpen === ModalType.CONFIRM_LEGAL_ASSOCIATION}
sender={{
senderId: formik.values.sender.id,
senderName: formik.values.sender.name,
}}
oldAddress={oldAddress}
newAddress={{
addressType: AddressType.LEGAL,
channelType: formik.values.channelType as ChannelType,
value: formik.values.s_value,
}}
onCancel={() => setModalOpen(null)}
onConfirm={() => handleAssociation()}
/>
<ExistingContactDialog
open={modalOpen === ModalType.EXISTING}
value={formik.values.s_value}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -163,14 +163,22 @@ const DigitalContactActivation: React.FC<Props> = ({ isTransferring = false }) =
<ConfirmationModal
open={modal.open}
title={t('courtesy-contacts.confirmation-modal-title')}
slots={{
confirmButton: Button,
closeButton: Button,
}}
slotsProps={{
closeButton: { onClick: handleConfirmationModalAccept, variant: 'contained' },
confirmButton: { onClick: handleConfirmationModalDecline, variant: 'outlined' },
closeButton: {
onClick: handleConfirmationModalAccept,
children: t(
`courtesy-contacts.confirmation-modal-${modal.step.toLowerCase()}-accept`
),
},
confirmButton: {
onClick: handleConfirmationModalDecline,
children: t('button.do-later', { ns: 'common' }),
},
}}
onCloseLabel={t(
`courtesy-contacts.confirmation-modal-${modal.step.toLowerCase()}-accept`
)}
onConfirmLabel={t('button.do-later', { ns: 'common' })}
>
<Trans
ns="recapiti"
Expand Down
Loading

0 comments on commit a07074a

Please sign in to comment.