feat(ui): validate-messages without side effects

This commit is contained in:
Thijs Louisse 2022-12-01 15:52:06 +01:00 committed by Thijs Louisse
parent f7fde3152a
commit f147024ce8
6 changed files with 252 additions and 199 deletions

View file

@ -0,0 +1,5 @@
---
'@lion/ui': patch
---
validate-messages without side effects

View file

@ -1,190 +1,11 @@
/* eslint-disable import/no-extraneous-dependencies */ /* eslint-disable import/no-extraneous-dependencies */
import { localize } from '@lion/ui/localize.js'; import { localize } from '@lion/ui/localize.js';
import { import { loadDefaultFeedbackMessagesNoSideEffects } from './loadDefaultFeedbackMessagesNoSideEffects.js';
DefaultSuccess,
IsDate,
IsDateDisabled,
MaxDate,
MinDate,
MinMaxDate,
IsNumber,
MaxNumber,
MinMaxNumber,
MinNumber,
Required,
EqualsLength,
IsEmail,
MaxLength,
MinLength,
MinMaxLength,
Pattern,
} from '@lion/ui/form-core.js';
import { PhoneNumber } from '@lion/ui/input-tel.js';
/** /**
* @typedef {import('../../form-core/types/validate/validate.js').FeedbackMessageData} FeedbackMessageData * @typedef {import('../../form-core/types/validate/validate.js').FeedbackMessageData} FeedbackMessageData
*/ */
let loaded = false;
export function loadDefaultFeedbackMessages() { export function loadDefaultFeedbackMessages() {
if (loaded === true) { return loadDefaultFeedbackMessagesNoSideEffects({ localize });
return;
}
const forMessagesToBeReady = () =>
localize.loadNamespace(
{
'lion-validate': /** @param {string} locale */ locale => {
switch (locale) {
case 'bg-BG':
return import('@lion/ui/validate-messages-translations/bg-BG.js');
case 'bg':
return import('@lion/ui/validate-messages-translations/bg.js');
case 'cs-CZ':
return import('@lion/ui/validate-messages-translations/cs-CZ.js');
case 'cs':
return import('@lion/ui/validate-messages-translations/cs.js');
case 'de-DE':
return import('@lion/ui/validate-messages-translations/de-DE.js');
case 'de':
return import('@lion/ui/validate-messages-translations/de.js');
case 'en-AU':
return import('@lion/ui/validate-messages-translations/en-AU.js');
case 'en-GB':
return import('@lion/ui/validate-messages-translations/en-GB.js');
case 'en-US':
return import('@lion/ui/validate-messages-translations/en-US.js');
case 'en-PH':
return import('@lion/ui/validate-messages-translations/en-PH.js');
case 'en':
return import('@lion/ui/validate-messages-translations/en.js');
case 'es-ES':
return import('@lion/ui/validate-messages-translations/es-ES.js');
case 'es':
return import('@lion/ui/validate-messages-translations/es.js');
case 'fr-FR':
return import('@lion/ui/validate-messages-translations/fr-FR.js');
case 'fr-BE':
return import('@lion/ui/validate-messages-translations/fr-BE.js');
case 'fr':
return import('@lion/ui/validate-messages-translations/fr.js');
case 'hu-HU':
return import('@lion/ui/validate-messages-translations/hu-HU.js');
case 'hu':
return import('@lion/ui/validate-messages-translations/hu.js');
case 'it-IT':
return import('@lion/ui/validate-messages-translations/it-IT.js');
case 'it':
return import('@lion/ui/validate-messages-translations/it.js');
case 'nl-BE':
return import('@lion/ui/validate-messages-translations/nl-BE.js');
case 'nl-NL':
return import('@lion/ui/validate-messages-translations/nl-NL.js');
case 'nl':
return import('@lion/ui/validate-messages-translations/nl.js');
case 'pl-PL':
return import('@lion/ui/validate-messages-translations/pl-PL.js');
case 'pl':
return import('@lion/ui/validate-messages-translations/pl.js');
case 'ro-RO':
return import('@lion/ui/validate-messages-translations/ro-RO.js');
case 'ro':
return import('@lion/ui/validate-messages-translations/ro.js');
case 'ru-RU':
return import('@lion/ui/validate-messages-translations/ru-RU.js');
case 'ru':
return import('@lion/ui/validate-messages-translations/ru.js');
case 'sk-SK':
return import('@lion/ui/validate-messages-translations/sk-SK.js');
case 'sk':
return import('@lion/ui/validate-messages-translations/sk.js');
case 'uk-UA':
return import('@lion/ui/validate-messages-translations/uk-UA.js');
case 'uk':
return import('@lion/ui/validate-messages-translations/uk.js');
case 'zh-CN':
case 'zh':
return import('@lion/ui/validate-messages-translations/zh.js');
default:
return import('@lion/ui/validate-messages-translations/en.js');
}
},
},
{ locale: localize.locale },
);
/**
* @param {FeedbackMessageData} data
* @returns {Promise<string|Element>}
*/
const getLocalizedMessage = async data => {
await forMessagesToBeReady();
if (data) {
return localize.msg(`lion-validate:${data.type}.${data.name}`, data);
}
return '';
};
/** @param {FeedbackMessageData} data */
Required.getMessage = async data => getLocalizedMessage(data);
/** @param {FeedbackMessageData} data */
EqualsLength.getMessage = async data => getLocalizedMessage(data);
/** @param {FeedbackMessageData} data */
MinLength.getMessage = async data => getLocalizedMessage(data);
/** @param {FeedbackMessageData} data */
MaxLength.getMessage = async data => getLocalizedMessage(data);
/** @param {FeedbackMessageData} data */
MinMaxLength.getMessage = async data => getLocalizedMessage(data);
/** @param {FeedbackMessageData} data */
Pattern.getMessage = async data => getLocalizedMessage(data);
/** @param {FeedbackMessageData} data */
IsEmail.getMessage = async data => getLocalizedMessage(data);
/** @param {FeedbackMessageData} data */
IsNumber.getMessage = async data => getLocalizedMessage(data);
/** @param {FeedbackMessageData} data */
MinNumber.getMessage = async data => getLocalizedMessage(data);
/** @param {FeedbackMessageData} data */
MaxNumber.getMessage = async data => getLocalizedMessage(data);
/** @param {FeedbackMessageData} data */
MinMaxNumber.getMessage = async data => getLocalizedMessage(data);
/** @param {FeedbackMessageData} data */
IsDate.getMessage = async data => getLocalizedMessage(data);
/** @param {FeedbackMessageData} data */
MinDate.getMessage = async data => getLocalizedMessage(data);
/** @param {FeedbackMessageData} data */
MaxDate.getMessage = async data => getLocalizedMessage(data);
/** @param {FeedbackMessageData} data */
MinMaxDate.getMessage = async data => getLocalizedMessage(data);
/** @param {FeedbackMessageData} data */
IsDateDisabled.getMessage = async data => getLocalizedMessage(data);
DefaultSuccess.getMessage = async data => {
await forMessagesToBeReady();
const randomKeys = localize.msg('lion-validate:success.RandomOk').split(',');
const key = randomKeys[Math.floor(Math.random() * randomKeys.length)].trim();
return localize.msg(`lion-validate:${key}`, data);
};
/** @param {FeedbackMessageData} data */
// @ts-ignore
PhoneNumber.getMessage = async data => {
await forMessagesToBeReady();
const { type, outcome } = data;
if (outcome === 'too-long') {
// TODO: get max-length of country and use MaxLength validator
return localize.msg(`lion-validate:${type}.Pattern`, data);
}
if (outcome === 'too-short') {
// TODO: get min-length of country and use MinLength validator
return localize.msg(`lion-validate:${type}.Pattern`, data);
}
// TODO: add a more specific message here
if (outcome === 'invalid-country-code') {
return localize.msg(`lion-validate:${type}.Pattern`, data);
}
return localize.msg(`lion-validate:${type}.Pattern`, data);
};
loaded = true;
} }

View file

@ -0,0 +1,194 @@
/* eslint-disable import/no-extraneous-dependencies */
import {
DefaultSuccess,
IsDate,
IsDateDisabled,
MaxDate,
MinDate,
MinMaxDate,
IsNumber,
MaxNumber,
MinMaxNumber,
MinNumber,
Required,
EqualsLength,
IsEmail,
MaxLength,
MinLength,
MinMaxLength,
Pattern,
} from '@lion/ui/form-core.js';
import { PhoneNumber } from '@lion/ui/input-tel.js';
/**
* @typedef {import('../../form-core/types/validate/validate.js').FeedbackMessageData} FeedbackMessageData
* @typedef {import('@lion/ui/localize.js').LocalizeManager} LocalizeManager
*/
let loaded = false;
/**
* @param {{localize: LocalizeManager}} opts allow multiple lion/extension lib versions to provide their deduped instance of LocalizeManager
* @returns
*/
export function loadDefaultFeedbackMessagesNoSideEffects({ localize }) {
if (loaded === true) {
return;
}
const forMessagesToBeReady = () =>
localize.loadNamespace(
{
'lion-validate': /** @param {string} locale */ locale => {
switch (locale) {
case 'bg-BG':
return import('@lion/ui/validate-messages-translations/bg-BG.js');
case 'bg':
return import('@lion/ui/validate-messages-translations/bg.js');
case 'cs-CZ':
return import('@lion/ui/validate-messages-translations/cs-CZ.js');
case 'cs':
return import('@lion/ui/validate-messages-translations/cs.js');
case 'de-DE':
return import('@lion/ui/validate-messages-translations/de-DE.js');
case 'de':
return import('@lion/ui/validate-messages-translations/de.js');
case 'en-AU':
return import('@lion/ui/validate-messages-translations/en-AU.js');
case 'en-GB':
return import('@lion/ui/validate-messages-translations/en-GB.js');
case 'en-US':
return import('@lion/ui/validate-messages-translations/en-US.js');
case 'en-PH':
return import('@lion/ui/validate-messages-translations/en-PH.js');
case 'en':
return import('@lion/ui/validate-messages-translations/en.js');
case 'es-ES':
return import('@lion/ui/validate-messages-translations/es-ES.js');
case 'es':
return import('@lion/ui/validate-messages-translations/es.js');
case 'fr-FR':
return import('@lion/ui/validate-messages-translations/fr-FR.js');
case 'fr-BE':
return import('@lion/ui/validate-messages-translations/fr-BE.js');
case 'fr':
return import('@lion/ui/validate-messages-translations/fr.js');
case 'hu-HU':
return import('@lion/ui/validate-messages-translations/hu-HU.js');
case 'hu':
return import('@lion/ui/validate-messages-translations/hu.js');
case 'it-IT':
return import('@lion/ui/validate-messages-translations/it-IT.js');
case 'it':
return import('@lion/ui/validate-messages-translations/it.js');
case 'nl-BE':
return import('@lion/ui/validate-messages-translations/nl-BE.js');
case 'nl-NL':
return import('@lion/ui/validate-messages-translations/nl-NL.js');
case 'nl':
return import('@lion/ui/validate-messages-translations/nl.js');
case 'pl-PL':
return import('@lion/ui/validate-messages-translations/pl-PL.js');
case 'pl':
return import('@lion/ui/validate-messages-translations/pl.js');
case 'ro-RO':
return import('@lion/ui/validate-messages-translations/ro-RO.js');
case 'ro':
return import('@lion/ui/validate-messages-translations/ro.js');
case 'ru-RU':
return import('@lion/ui/validate-messages-translations/ru-RU.js');
case 'ru':
return import('@lion/ui/validate-messages-translations/ru.js');
case 'sk-SK':
return import('@lion/ui/validate-messages-translations/sk-SK.js');
case 'sk':
return import('@lion/ui/validate-messages-translations/sk.js');
case 'uk-UA':
return import('@lion/ui/validate-messages-translations/uk-UA.js');
case 'uk':
return import('@lion/ui/validate-messages-translations/uk.js');
case 'zh-CN':
case 'zh':
return import('@lion/ui/validate-messages-translations/zh.js');
default:
return import('@lion/ui/validate-messages-translations/en.js');
}
},
},
{ locale: localize.locale },
);
/**
* @param {FeedbackMessageData} data
* @returns {Promise<string|Element>}
*/
const getLocalizedMessage = async data => {
await forMessagesToBeReady();
if (data) {
return localize.msg(`lion-validate:${data.type}.${data.name}`, data);
}
return '';
};
/** @param {FeedbackMessageData} data */
Required.getMessage = async data => getLocalizedMessage(data);
/** @param {FeedbackMessageData} data */
EqualsLength.getMessage = async data => getLocalizedMessage(data);
/** @param {FeedbackMessageData} data */
MinLength.getMessage = async data => getLocalizedMessage(data);
/** @param {FeedbackMessageData} data */
MaxLength.getMessage = async data => getLocalizedMessage(data);
/** @param {FeedbackMessageData} data */
MinMaxLength.getMessage = async data => getLocalizedMessage(data);
/** @param {FeedbackMessageData} data */
Pattern.getMessage = async data => getLocalizedMessage(data);
/** @param {FeedbackMessageData} data */
IsEmail.getMessage = async data => getLocalizedMessage(data);
/** @param {FeedbackMessageData} data */
IsNumber.getMessage = async data => getLocalizedMessage(data);
/** @param {FeedbackMessageData} data */
MinNumber.getMessage = async data => getLocalizedMessage(data);
/** @param {FeedbackMessageData} data */
MaxNumber.getMessage = async data => getLocalizedMessage(data);
/** @param {FeedbackMessageData} data */
MinMaxNumber.getMessage = async data => getLocalizedMessage(data);
/** @param {FeedbackMessageData} data */
IsDate.getMessage = async data => getLocalizedMessage(data);
/** @param {FeedbackMessageData} data */
MinDate.getMessage = async data => getLocalizedMessage(data);
/** @param {FeedbackMessageData} data */
MaxDate.getMessage = async data => getLocalizedMessage(data);
/** @param {FeedbackMessageData} data */
MinMaxDate.getMessage = async data => getLocalizedMessage(data);
/** @param {FeedbackMessageData} data */
IsDateDisabled.getMessage = async data => getLocalizedMessage(data);
DefaultSuccess.getMessage = async data => {
await forMessagesToBeReady();
const randomKeys = localize.msg('lion-validate:success.RandomOk').split(',');
const key = randomKeys[Math.floor(Math.random() * randomKeys.length)].trim();
return localize.msg(`lion-validate:${key}`, data);
};
/** @param {FeedbackMessageData} data */
// @ts-ignore
PhoneNumber.getMessage = async data => {
await forMessagesToBeReady();
const { type, outcome } = data;
if (outcome === 'too-long') {
// TODO: get max-length of country and use MaxLength validator
return localize.msg(`lion-validate:${type}.Pattern`, data);
}
if (outcome === 'too-short') {
// TODO: get min-length of country and use MinLength validator
return localize.msg(`lion-validate:${type}.Pattern`, data);
}
// TODO: add a more specific message here
if (outcome === 'invalid-country-code') {
return localize.msg(`lion-validate:${type}.Pattern`, data);
}
return localize.msg(`lion-validate:${type}.Pattern`, data);
};
loaded = true;
}

View file

@ -1,6 +1,5 @@
/* eslint-disable no-unused-vars, no-param-reassign */ /* eslint-disable no-unused-vars, no-param-reassign */
import { expect } from '@open-wc/testing'; import { expect } from '@open-wc/testing';
import { localize } from '@lion/ui/localize.js';
import { Required } from '@lion/ui/form-core.js'; import { Required } from '@lion/ui/form-core.js';
import { loadDefaultFeedbackMessages } from '@lion/ui/validate-messages.js'; import { loadDefaultFeedbackMessages } from '@lion/ui/validate-messages.js';
@ -20,7 +19,7 @@ function getProtectedMembers(validatorEl) {
} }
describe('loadDefaultFeedbackMessages', () => { describe('loadDefaultFeedbackMessages', () => {
it('will set default feedback message for Required', async () => { it('will work without providing a LocalizeManager instance', async () => {
const el = new Required(); const el = new Required();
const { getMessage } = getProtectedMembers(el); const { getMessage } = getProtectedMembers(el);
expect(await getMessage()).to.equals( expect(await getMessage()).to.equals(
@ -30,20 +29,4 @@ describe('loadDefaultFeedbackMessages', () => {
loadDefaultFeedbackMessages(); loadDefaultFeedbackMessages();
expect(await getMessage({ fieldName: 'password' })).to.equal('Please enter a(n) password.'); expect(await getMessage({ fieldName: 'password' })).to.equal('Please enter a(n) password.');
}); });
it('will await loading of translations when switching locale', async () => {
const el = new Required();
const { getMessage } = getProtectedMembers(el);
loadDefaultFeedbackMessages();
expect(await getMessage({ fieldName: 'password' })).to.equal('Please enter a(n) password.');
expect(await getMessage({ fieldName: 'user name' })).to.equal('Please enter a(n) user name.');
localize.locale = 'de-DE';
expect(await getMessage({ fieldName: 'Password' })).to.equal(
'Password muss ausgefüllt werden.',
);
expect(await getMessage({ fieldName: 'Benutzername' })).to.equal(
'Benutzername muss ausgefüllt werden.',
);
});
}); });

View file

@ -0,0 +1,49 @@
/* eslint-disable no-unused-vars, no-param-reassign */
import { expect } from '@open-wc/testing';
import { localize } from '@lion/ui/localize.js';
import { Required } from '@lion/ui/form-core.js';
import { loadDefaultFeedbackMessagesNoSideEffects } from '@lion/ui/validate-messages-no-side-effects.js';
/**
* @typedef {import('../../form-core/src/validate/Validator.js').Validator} Validator
*/
/**
* @param {Validator} validatorEl
*/
function getProtectedMembers(validatorEl) {
// @ts-ignore protected members allowed in test
return {
// @ts-ignore
getMessage: (...args) => validatorEl._getMessage(...args),
};
}
describe('loadDefaultFeedbackMessagesNoSideEffects', () => {
it('will set default feedback message for Required', async () => {
const el = new Required();
const { getMessage } = getProtectedMembers(el);
expect(await getMessage()).to.equals(
'Please configure an error message for "Required" by overriding "static async getMessage()"',
);
loadDefaultFeedbackMessagesNoSideEffects({ localize });
expect(await getMessage({ fieldName: 'password' })).to.equal('Please enter a(n) password.');
});
it('will await loading of translations when switching locale', async () => {
const el = new Required();
const { getMessage } = getProtectedMembers(el);
loadDefaultFeedbackMessagesNoSideEffects({ localize });
expect(await getMessage({ fieldName: 'password' })).to.equal('Please enter a(n) password.');
expect(await getMessage({ fieldName: 'user name' })).to.equal('Please enter a(n) user name.');
localize.locale = 'de-DE';
expect(await getMessage({ fieldName: 'Password' })).to.equal(
'Password muss ausgefüllt werden.',
);
expect(await getMessage({ fieldName: 'Benutzername' })).to.equal(
'Benutzername muss ausgefüllt werden.',
);
});
});

View file

@ -0,0 +1 @@
export { loadDefaultFeedbackMessagesNoSideEffects } from '../components/validate-messages/src/loadDefaultFeedbackMessagesNoSideEffects.js';