Skip to content

Commit

Permalink
feat(orga): add a modal to force invitation confirmation
Browse files Browse the repository at this point in the history
  • Loading branch information
EmmanuelleBonnemay committed Jan 23, 2025
1 parent 9be578b commit 4214ce0
Show file tree
Hide file tree
Showing 7 changed files with 174 additions and 7 deletions.
27 changes: 26 additions & 1 deletion orga/app/components/team/invite-form.hbs
Original file line number Diff line number Diff line change
Expand Up @@ -6,21 +6,46 @@
@id="email"
type="email"
@value={{@email}}
aria-invalid={{if this.emailError "true" "false"}}
aria-describedby="email-error"
class="invite-form__email-field"
@requiredLabel={{t "common.form.mandatory-fields-title"}}
{{on "change" @onUpdateEmail}}
>
<:label>{{t "pages.team-new-item.input-label"}}</:label>
</PixTextarea>
{{#if this.emailError}}
<p class="invite-form__error-message">{{this.emailError}}</p>
{{/if}}
</div>

<div class="form__validation">
<PixButton @triggerAction={{@onCancel}} @variant="secondary">
{{t "common.actions.cancel"}}
</PixButton>
<PixButton @type="submit" @isLoading={{@isLoading}}>
<PixButton @triggerAction={{this.openModal}} @variant="secondary">
{{t "pages.team-new-item.invite-button"}}
</PixButton>
</div>

<PixModal
class="invite-form__modal"
@title={{t "pages.team-new.invite-form-modal.title"}}
@showModal={{this.modalOpen}}
@onCloseButtonClick={{this.closeModal}}
>
<:content>
<p>{{t "pages.team-new.invite-form-modal.warning"}}</p>
<p>{{t "pages.team-new.invite-form-modal.question"}}</p>
</:content>

<:footer>
<PixButton @variant="secondary" @isBorderVisible={{true}} @triggerAction={{this.closeModal}}>
{{t "common.actions.cancel"}}
</PixButton>
<PixButton @variant="secondary" @triggerAction={{@onSubmit}} @isLoading={{this.isLoading}}>{{t
"pages.team-new.invite-form-modal.confirm"
}}</PixButton>
</:footer>
</PixModal>
</form>
35 changes: 35 additions & 0 deletions orga/app/components/team/invite-form.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import { action } from '@ember/object';
import { service } from '@ember/service';
import Component from '@glimmer/component';
import { tracked } from '@glimmer/tracking';

import isEmailValid from '../../utils/email-validator';

export default class InviteForm extends Component {
@service intl;
@tracked modalOpen = false;
@tracked emailError = null;

@action
openModal() {
const emailInput = this.args?.email?.trim();
if (!emailInput) {
this.emailError = this.intl.t('pages.team-new.errors.mandatory-email-field');
return;
}
const emails = emailInput.split(',').map((email) => email.trim());
const areEmailsValid = emails.every((email) => isEmailValid(email));

if (!areEmailsValid) {
this.emailError = this.intl.t('pages.team-new.errors.invalid-input');
return;
}
this.emailError = null;
this.modalOpen = true;
}

@action
closeModal() {
this.modalOpen = false;
}
}
1 change: 1 addition & 0 deletions orga/app/styles/app.scss
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@
@use 'components/register-form' as *;
@use 'components/search-input' as *;
@use 'components/team' as *;
@use 'components/team/invite-form' as *;
@use 'components/login-or-register' as *;
@use 'components/manage-authentication-method-modal' as *;
@use 'components/participation-filters' as *;
Expand Down
13 changes: 13 additions & 0 deletions orga/app/styles/components/team/invite-form.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
.invite-form {

&__email-field {
min-height: 70px;
}

&__error-message {
margin-top: 0.5rem;
color: var(--pix-error-700);
font-size: 0.875rem;
line-height: 1.2;
}
}
3 changes: 0 additions & 3 deletions orga/app/styles/pages/authenticated/team/new.scss
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,4 @@
}
}

.invite-form__email-field {
min-height: 70px;
}

23 changes: 23 additions & 0 deletions orga/tests/acceptance/team-creation-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import { module, test } from 'qunit';
import authenticateSession from '../helpers/authenticate-session';
import setupIntl from '../helpers/setup-intl';
import { createPrescriberByUser, createUserMembershipWithRole } from '../helpers/test-init';
import { waitForDialog } from '../helpers/wait-for';

module('Acceptance | Team Creation', function (hooks) {
setupApplicationTest(hooks);
Expand Down Expand Up @@ -55,6 +56,7 @@ module('Acceptance | Team Creation', function (hooks) {
let inputLabel;
let cancelButton;
let inviteButton;
let confirmButton;

hooks.beforeEach(async function () {
user = createUserMembershipWithRole('ADMIN');
Expand All @@ -67,6 +69,7 @@ module('Acceptance | Team Creation', function (hooks) {
inputLabel = `${t('pages.team-new-item.input-label')} *`;
inviteButton = t('pages.team-new-item.invite-button');
cancelButton = t('common.actions.cancel');
confirmButton = t('pages.team-new.invite-form-modal.confirm');
});

test('it should be accessible', async function (assert) {
Expand Down Expand Up @@ -95,6 +98,13 @@ module('Acceptance | Team Creation', function (hooks) {
await clickByName(inviteButton);

// then
await waitForDialog();
await waitForDialog();

//when
await clickByName(confirmButton);

//then
const organizationInvitation = server.db.organizationInvitations[server.db.organizationInvitations.length - 1];

assert.strictEqual(organizationInvitation.email, email);
Expand All @@ -118,6 +128,9 @@ module('Acceptance | Team Creation', function (hooks) {
// when
await clickByName(inviteButton);

await waitForDialog();
await clickByName(confirmButton);

// then
assert.ok(screen.getByText(t('pages.team-new.success.multiple-invitations')));
});
Expand Down Expand Up @@ -182,6 +195,8 @@ module('Acceptance | Team Creation', function (hooks) {

// when
await clickByName(inviteButton);
await waitForDialog();
await clickByName(confirmButton);

// then

Expand Down Expand Up @@ -211,6 +226,8 @@ module('Acceptance | Team Creation', function (hooks) {

// when
await clickByName(inviteButton);
await waitForDialog();
await clickByName(confirmButton);

// then

Expand Down Expand Up @@ -240,6 +257,8 @@ module('Acceptance | Team Creation', function (hooks) {

// when
await clickByName(inviteButton);
await waitForDialog();
await clickByName(confirmButton);

// then

Expand Down Expand Up @@ -269,6 +288,8 @@ module('Acceptance | Team Creation', function (hooks) {

// when
await clickByName(inviteButton);
await waitForDialog();
await clickByName(confirmButton);

// then

Expand Down Expand Up @@ -302,6 +323,8 @@ module('Acceptance | Team Creation', function (hooks) {

// When
await clickByName(inviteButton);
await waitForDialog();
await clickByName(confirmButton);

// Then
const expectedErrorMessage = t('pages.team-new.errors.sending-email-to-invalid-email-address', {
Expand Down
79 changes: 76 additions & 3 deletions orga/tests/integration/components/team/invite-form-test.js
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
import { fillByLabel, render } from '@1024pix/ember-testing-library';
import { click } from '@ember/test-helpers';
import { hbs } from 'ember-cli-htmlbars';
import { t } from 'ember-intl/test-support';
import { module, test } from 'qunit';
import sinon from 'sinon';

import setupIntlRenderingTest from '../../../helpers/setup-intl-rendering';
import { waitForDialog } from '../../../helpers/wait-for';

module('Integration | Component | Team::InviteForm', function (hooks) {
setupIntlRenderingTest(hooks);
Expand All @@ -17,16 +19,16 @@ module('Integration | Component | Team::InviteForm', function (hooks) {

test('it should contain email input and validation button', async function (assert) {
// when
await render(
const screen = await render(
hbs`<Team::InviteForm @onSubmit={{this.inviteSpy}} @onCancel={{this.cancelSpy}} @onUpdateEmail={{this.updateEmail}} />`,
);

// then
assert.dom('#email').exists();
assert.dom('#email').isRequired();
assert.dom('button[type="submit"]').exists();
assert.ok(screen.getByText(t('pages.team-new-item.invite-button')));
assert.dom(screen.queryByRole('dialog')).doesNotExist();
});

test('it should bind organizationInvitation properties with email form input', async function (assert) {
// given
this.set('email', '[email protected]');
Expand All @@ -46,4 +48,75 @@ module('Integration | Component | Team::InviteForm', function (hooks) {
// then
assert.ok(this.updateEmail.called);
});
test('it should open confirmation modal when invite button is clicked', async function (assert) {
// given
this.set('email', '[email protected]');
const screen = await render(
hbs`<Team::InviteForm
@email={{this.email}}
@onSubmit={{this.inviteSpy}}
@onCancel={{this.cancelSpy}}
@onUpdateEmail={{this.updateEmail}}
/>`,
);

// when
const inputLabel = `${t('pages.team-new-item.input-label')} *`;
await fillByLabel(inputLabel, '[email protected]');
const inviteButton = await screen.findByRole('button', {
name: t('pages.team-new-item.invite-button'),
});
await click(inviteButton);
await waitForDialog();
// then
assert.dom(screen.getByRole('dialog')).isVisible();
});
test('it should display error message when no email is in input and invite button is clicked', async function (assert) {
// given
//this.set('email', '[email protected]');
this.set('email', '');
const errorMessage = t('pages.team-new.errors.mandatory-email-field');
const screen = await render(
hbs`<Team::InviteForm
@email={{this.email}}
@onSubmit={{this.inviteSpy}}
@onCancel={{this.cancelSpy}}
@onUpdateEmail={{this.updateEmail}}
/>`,
);

// when
const inputLabel = `${t('pages.team-new-item.input-label')} *`;
await fillByLabel(inputLabel, '');
const inviteButton = await screen.findByRole('button', {
name: t('pages.team-new-item.invite-button'),
});
await click(inviteButton);
// then
assert.dom(await screen.findByText(errorMessage)).exists();
assert.dom(screen.queryByRole('dialog')).doesNotExist();
});
test('it should display error message when email format is not correct', async function (assert) {
// given
this.set('email', '[email protected];alex-mail.incorrect');
const errorMessage = t('pages.team-new.errors.invalid-input');
const screen = await render(
hbs`<Team::InviteForm
@email={{this.email}}
@onSubmit={{this.inviteSpy}}
@onCancel={{this.cancelSpy}}
@onUpdateEmail={{this.updateEmail}}
/>`,
);

// when
const inviteButton = await screen.findByRole('button', {
name: t('pages.team-new-item.invite-button'),
});

await click(inviteButton);
// then
assert.dom(await screen.findByText(errorMessage)).exists();
assert.dom(screen.queryByRole('dialog')).doesNotExist();
});
});

0 comments on commit 4214ce0

Please sign in to comment.