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 8, 2024
1 parent 049ee5b commit ab93546
Show file tree
Hide file tree
Showing 7 changed files with 195 additions and 2 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
import Component from '@glimmer/component';
import { t } from 'ember-intl';

import PasswordRule from './password-rule';

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

get rulesNumber() {
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.checklistRules as |checklistRule|}}
<PasswordRule
@key={{checklistRule.key}}
@description={{checklistRule.description}}
@isValid={{checklistRule.isValid}}
@errors={{@errors}}
@value={{@value}}
/>
{{/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
rulesNumber=this.rulesNumber
}}
</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 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, rulesNumber: 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, rulesNumber: 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, rulesNumber: 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 @@ -125,6 +125,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 { rulesNumber } requirements completed.",
"contains-digit": "a minimum figure",
"contains-lowercase": "at least one lower case letter",
"contains-uppercase": "at least one capital letter",
"min-length": "at least 8 characters"
}
}
},
"invited": {
Expand Down
13 changes: 12 additions & 1 deletion mon-pix/translations/es.json
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,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 { rulesNumber } requirements completed.",
"contains-digit": "a minimum figure",
"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": {
"seeAnswer" : "ver la respuesta",
"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 @@ -125,6 +125,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 { rulesNumber } 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 @@ -118,6 +118,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 { rulesNumber } requirements completed.",
"contains-digit": "a minimum figure",
"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": {
"seeAnswer" : "Zie het antwoord",
"seeAnswer": "Zie het antwoord",
"seeAgain": "Bekijk de vraag",
"nextCard": "Volgende"
},
Expand Down

0 comments on commit ab93546

Please sign in to comment.