Skip to content

Commit

Permalink
feat(mon-pix): add password checklist component
Browse files Browse the repository at this point in the history
  • Loading branch information
er-lim authored and bpetetot committed Oct 10, 2024
1 parent b8b87e4 commit 4bf83c6
Show file tree
Hide file tree
Showing 7 changed files with 190 additions and 3 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import Component from '@glimmer/component';
import { t } from 'ember-intl';

import PasswordRule from './password-rule';

export default class PasswordChecklist extends Component {
get rules() {
const { rules, value, errors } = this.args;
return rules.map((rule) => ({
...rule,
isValid: Boolean(value) && !errors?.includes(rule.key),
}));
}

get rulesCount() {
const { rules } = this.args;
return rules.length;
}

get rulesCompleted() {
const { rules, value, errors } = this.args;
return !value ? 0 : rules.length - errors?.length;
}

<template>
<div class="password-checklist" ...attributes>
<label class="password-checklist__instructions" for="checklist">
{{t "components.authentication.password-input.instructions-label"}}
</label>
<ul id="checklist">
{{#each this.rules as |rule|}}
<PasswordRule @description={{rule.description}} @isValid={{rule.isValid}} />
{{/each}}
</ul>
<p class="sr-only" aria-atomic="true" aria-relevant="all" aria-live="polite">
{{t
"components.authentication.password-input.rules.completed-message"
rulesCompleted=this.rulesCompleted
rulesCount=this.rulesCount
}}
</p>
</div>
</template>
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
.password-checklist {
@extend %pix-body-s;

display: flex;
flex-direction: column;
padding: var(--pix-spacing-2x) var(--pix-spacing-4x);
background-color: var(--pix-neutral-20);
border-radius: var(--pix-spacing-2x);

&__instructions {
margin-bottom: var(--pix-spacing-2x);
font-weight: var(--pix-font-bold);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
import { render } from '@1024pix/ember-testing-library';
import { t } from 'ember-intl/test-support';
import PasswordChecklist from 'mon-pix/components/authentication/password-input/password-checklist';
import { module, test } from 'qunit';

import setupIntlRenderingTest from '../../../../helpers/setup-intl-rendering';

const I18N = {
RULES_STATUS_MESSAGE: 'components.authentication.password-input.rules.completed-message',
};

module('Integration | Component | authentication | password-input | password-checklist', function (hooks) {
setupIntlRenderingTest(hooks);

module('when a password value is set', function () {
module('when there is no validation error', function () {
test('it displays rules and indicates that all rules completed', async function (assert) {
// given
const value = 'my-value';
const rules = [
{ key: 'rule1', description: 'Rule 1' },
{ key: 'rule2', description: 'Rule 2' },
];
const errors = [];

// when
const screen = await render(
<template><PasswordChecklist @value={{value}} @rules={{rules}} @errors={{errors}} /></template>,
);

// then
const rule1Element = screen.getByRole('listitem', { name: 'Rule 1.' });
assert.dom(rule1Element).exists();

const rule2Element = screen.getByRole('listitem', { name: 'Rule 2.' });
assert.dom(rule2Element).exists();

const rulesStatusMessage = t(I18N.RULES_STATUS_MESSAGE, { rulesCompleted: 2, rulesCount: 2 });
assert.dom(screen.getByText(rulesStatusMessage)).exists();
});
});

module('when there is a validation error', function () {
test('it indicates number of valid rules completed', async function (assert) {
// given
const value = 'my-value';
const rules = [
{ key: 'rule1', description: 'Rule 1' },
{ key: 'rule2', description: 'Rule 2' },
];
const errors = ['rule1'];

// when
const screen = await render(
<template><PasswordChecklist @value={{value}} @rules={{rules}} @errors={{errors}} /></template>,
);

// then
const rulesStatusMessage = t(I18N.RULES_STATUS_MESSAGE, { rulesCompleted: 1, rulesCount: 2 });
assert.dom(screen.getByText(rulesStatusMessage)).exists();
});
});
});

module('when no password value is set', function () {
test('it indicates no rule is completed', async function (assert) {
// given
const value = null;
const rules = [
{ key: 'rule1', description: 'Rule 1' },
{ key: 'rule2', description: 'Rule 2' },
];
const errors = [];

// when
const screen = await render(
<template><PasswordChecklist @value={{value}} @rules={{rules}} @errors={{errors}} /></template>,
);

// then
const rulesStatusMessage = t(I18N.RULES_STATUS_MESSAGE, { rulesCompleted: 0, rulesCount: 2 });
assert.dom(screen.getByText(rulesStatusMessage)).exists();
});
});
});
11 changes: 11 additions & 0 deletions mon-pix/translations/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,17 @@
"label": "Search for an organization",
"placeholder": "Select an organization",
"searchLabel": "Keyword search"
},
"password-input": {
"error-message": "You have entered the wrong password. Your password must be longer than 8 characters and contain at least one number, one lower case letter and one upper case letter.",
"instructions-label": "Your password must comply with the following rules:",
"rules": {
"completed-message": "{ rulesCompleted } out of { rulesCount } requirements completed.",
"contains-digit": "at least one number",
"contains-lowercase": "at least one lower case letter",
"contains-uppercase": "at least one capital letter",
"min-length": "at least 8 characters"
}
}
},
"invited": {
Expand Down
15 changes: 13 additions & 2 deletions mon-pix/translations/es.json
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,17 @@
"label": "Search for an organization",
"placeholder": "Select an organization",
"searchLabel": "Keyword search"
},
"password-input": {
"error-message": "You have entered the wrong password. Your password must be longer than 8 characters and contain at least one number, one lower case letter and one upper case letter.",
"instructions-label": "Your password must comply with the following rules:",
"rules": {
"completed-message": "{ rulesCompleted } out of { rulesCount } requirements completed.",
"contains-digit": "at least one number",
"contains-lowercase": "at least one lower case letter",
"contains-uppercase": "at least one capital letter",
"min-length": "at least 8 characters"
}
}
},
"invited": {
Expand Down Expand Up @@ -1357,8 +1368,8 @@
}
},
"flashcards": {
"start" : "Para empezar",
"seeAnswer" : "ver la respuesta",
"start": "Para empezar",
"seeAnswer": "ver la respuesta",
"seeAgain": "Revisa la pregunta",
"nextCard": "Siguiente"
},
Expand Down
11 changes: 11 additions & 0 deletions mon-pix/translations/fr.json
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,17 @@
"label": "Rechercher une organisation",
"placeholder": "Sélectionner un organisme",
"searchLabel": "Recherche par mots-clés"
},
"password-input": {
"error-message": "Le mot de passe saisi est erroné. Votre mot de passe doit être supérieur à 8 caractères et contenir au minimum un chiffre, une minuscule et une majuscule.",
"instructions-label": "Votre mot de passe doit respecter les règles suivantes :",
"rules": {
"completed-message": "{ rulesCompleted } sur { rulesCount } complétées.",
"contains-digit": "un chiffre minimum",
"contains-lowercase": "une minuscule minimum",
"contains-uppercase": "une majuscule minimum",
"min-length": "8 caractères minimum"
}
}
},
"invited": {
Expand Down
13 changes: 12 additions & 1 deletion mon-pix/translations/nl.json
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,17 @@
"label": "Search for an organization",
"placeholder": "Select an organization",
"searchLabel": "Keyword search"
},
"password-input": {
"error-message": "You have entered the wrong password. Your password must be longer than 8 characters and contain at least one number, one lower case letter and one upper case letter.",
"instructions-label": "Your password must comply with the following rules:",
"rules": {
"completed-message": "{ rulesCompleted } out of { rulesCount } requirements completed.",
"contains-digit": "at least one number",
"contains-lowercase": "at least one lower case letter",
"contains-uppercase": "at least one capital letter",
"min-length": "at least 8 characters"
}
}
},
"invited": {
Expand Down Expand Up @@ -1358,7 +1369,7 @@
},
"flashcards": {
"start": "Beginnen",
"seeAnswer" : "Zie het antwoord",
"seeAnswer": "Zie het antwoord",
"seeAgain": "Bekijk de vraag",
"nextCard": "Volgende"
},
Expand Down

0 comments on commit 4bf83c6

Please sign in to comment.