feat(localize): await loadingComplete before sending locale changed
This commit is contained in:
parent
09ee55cbee
commit
c544af4eb9
7 changed files with 64 additions and 28 deletions
5
.changeset/unlucky-cameras-joke.md
Normal file
5
.changeset/unlucky-cameras-joke.md
Normal file
|
|
@ -0,0 +1,5 @@
|
||||||
|
---
|
||||||
|
'@lion/localize': minor
|
||||||
|
---
|
||||||
|
|
||||||
|
BREAKING: Fires localeChanged event (and as a result, invokes onLocaleChanged / onLocaleUpdated) after localize loading has completed. This means if the user switches the locale to a locale which has not loaded yet, it will load it first before sending the event. This will allow users to immediately call localize.msg and get the right output, without having to await localize.loadingComplete themselves. This is slightly breaking with regards to timing and might break tests in extensions. In that case, you probably need a `await localize.loadingComplete` statement in front of the failing assertion.
|
||||||
|
|
@ -1393,6 +1393,7 @@ describe('<lion-calendar>', () => {
|
||||||
]);
|
]);
|
||||||
|
|
||||||
localize.locale = 'nl-NL';
|
localize.locale = 'nl-NL';
|
||||||
|
await localize.loadingComplete;
|
||||||
await el.updateComplete;
|
await el.updateComplete;
|
||||||
expect(elObj.nextMonthButtonEl?.getAttribute('aria-label')).to.equal(
|
expect(elObj.nextMonthButtonEl?.getAttribute('aria-label')).to.equal(
|
||||||
'Volgende maand, december 2019',
|
'Volgende maand, december 2019',
|
||||||
|
|
|
||||||
|
|
@ -332,6 +332,7 @@ export function runValidateMixinFeedbackPart() {
|
||||||
expect(_feedbackNode.feedbackData?.[0].message).to.equal('Message for MinLength');
|
expect(_feedbackNode.feedbackData?.[0].message).to.equal('Message for MinLength');
|
||||||
|
|
||||||
localize.locale = 'de-DE';
|
localize.locale = 'de-DE';
|
||||||
|
await localize.loadingComplete;
|
||||||
await el.feedbackComplete;
|
await el.feedbackComplete;
|
||||||
expect(_feedbackNode.feedbackData?.[0].message).to.equal('Nachricht für MinLength');
|
expect(_feedbackNode.feedbackData?.[0].message).to.equal('Nachricht für MinLength');
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -164,12 +164,14 @@ describe('Form Validation Integrations', () => {
|
||||||
expect(_feedbackNode.feedbackData?.[0].message).to.equal('Please enter a(n) Text.');
|
expect(_feedbackNode.feedbackData?.[0].message).to.equal('Please enter a(n) Text.');
|
||||||
|
|
||||||
localize.locale = 'nl-NL';
|
localize.locale = 'nl-NL';
|
||||||
|
await localize.loadingComplete;
|
||||||
await el.updateComplete;
|
await el.updateComplete;
|
||||||
await el.feedbackComplete;
|
await el.feedbackComplete;
|
||||||
expect(el.label).to.equal('Tekst');
|
expect(el.label).to.equal('Tekst');
|
||||||
expect(_feedbackNode.feedbackData?.[0].message).to.equal('Vul een Tekst in.');
|
expect(_feedbackNode.feedbackData?.[0].message).to.equal('Vul een Tekst in.');
|
||||||
|
|
||||||
localize.locale = 'en-GB';
|
localize.locale = 'en-GB';
|
||||||
|
await localize.loadingComplete;
|
||||||
await el.updateComplete;
|
await el.updateComplete;
|
||||||
await el.feedbackComplete;
|
await el.feedbackComplete;
|
||||||
expect(el.label).to.equal('Text');
|
expect(el.label).to.equal('Text');
|
||||||
|
|
|
||||||
|
|
@ -41,7 +41,7 @@ export class LocalizeManager {
|
||||||
this.__namespaceLoadersCache = {};
|
this.__namespaceLoadersCache = {};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @type {Object.<string, Object.<string, Promise.<Object>>>}
|
* @type {Object.<string, Object.<string, Promise.<Object|void>>>}
|
||||||
* @private
|
* @private
|
||||||
*/
|
*/
|
||||||
this.__namespaceLoaderPromisesCache = {};
|
this.__namespaceLoaderPromisesCache = {};
|
||||||
|
|
@ -419,7 +419,7 @@ export class LocalizeManager {
|
||||||
/**
|
/**
|
||||||
* @param {string} locale
|
* @param {string} locale
|
||||||
* @param {string} namespace
|
* @param {string} namespace
|
||||||
* @param {Promise.<Object>} promise
|
* @param {Promise.<Object|void>} promise
|
||||||
* @protected
|
* @protected
|
||||||
*/
|
*/
|
||||||
_cacheNamespaceLoaderPromise(locale, namespace, promise) {
|
_cacheNamespaceLoaderPromise(locale, namespace, promise) {
|
||||||
|
|
@ -495,32 +495,30 @@ export class LocalizeManager {
|
||||||
}
|
}
|
||||||
if (this._autoLoadOnLocaleChange) {
|
if (this._autoLoadOnLocaleChange) {
|
||||||
this._loadAllMissing(newLocale, oldLocale);
|
this._loadAllMissing(newLocale, oldLocale);
|
||||||
|
this.loadingComplete.then(() => {
|
||||||
|
this.dispatchEvent(new CustomEvent('localeChanged', { detail: { newLocale, oldLocale } }));
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
this.dispatchEvent(new CustomEvent('localeChanged', { detail: { newLocale, oldLocale } }));
|
||||||
}
|
}
|
||||||
this.dispatchEvent(new CustomEvent('localeChanged', { detail: { newLocale, oldLocale } }));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {string} newLocale
|
* @param {string} newLocale
|
||||||
* @param {string} oldLocale
|
* @param {string} oldLocale
|
||||||
* @returns {Promise.<Object>}
|
|
||||||
* @protected
|
* @protected
|
||||||
*/
|
*/
|
||||||
_loadAllMissing(newLocale, oldLocale) {
|
_loadAllMissing(newLocale, oldLocale) {
|
||||||
const oldLocaleNamespaces = this.__storage[oldLocale] || {};
|
const oldLocaleNamespaces = this.__storage[oldLocale] || {};
|
||||||
const newLocaleNamespaces = this.__storage[newLocale] || {};
|
const newLocaleNamespaces = this.__storage[newLocale] || {};
|
||||||
/** @type {Promise<Object|void>[]} */
|
|
||||||
const promises = [];
|
|
||||||
Object.keys(oldLocaleNamespaces).forEach(namespace => {
|
Object.keys(oldLocaleNamespaces).forEach(namespace => {
|
||||||
const newNamespaceData = newLocaleNamespaces[namespace];
|
const newNamespaceData = newLocaleNamespaces[namespace];
|
||||||
if (!newNamespaceData) {
|
if (!newNamespaceData) {
|
||||||
promises.push(
|
this.loadNamespace(namespace, {
|
||||||
this.loadNamespace(namespace, {
|
locale: newLocale,
|
||||||
locale: newLocale,
|
});
|
||||||
}),
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
return Promise.all(promises);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
||||||
|
|
@ -21,7 +21,7 @@ export function setupEmptyFakeImportsFor(namespaces, locales) {
|
||||||
namespaces.forEach(namespace => {
|
namespaces.forEach(namespace => {
|
||||||
locales.forEach(locale => {
|
locales.forEach(locale => {
|
||||||
setupFakeImport(`./${namespace}/${locale}.js`, {
|
setupFakeImport(`./${namespace}/${locale}.js`, {
|
||||||
default: {},
|
default: { foo: `bar-${locale}` },
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -29,7 +29,6 @@ describe('LocalizeMixin', () => {
|
||||||
};
|
};
|
||||||
|
|
||||||
const tagString = defineCE(
|
const tagString = defineCE(
|
||||||
// @ts-ignore
|
|
||||||
class MyElement extends LocalizeMixin(LitElement) {
|
class MyElement extends LocalizeMixin(LitElement) {
|
||||||
static get localizeNamespaces() {
|
static get localizeNamespaces() {
|
||||||
return [myElementNs, ...super.localizeNamespaces];
|
return [myElementNs, ...super.localizeNamespaces];
|
||||||
|
|
@ -97,7 +96,6 @@ describe('LocalizeMixin', () => {
|
||||||
'my-element': locale => fakeImport(`./my-element/${locale}.js`),
|
'my-element': locale => fakeImport(`./my-element/${locale}.js`),
|
||||||
};
|
};
|
||||||
|
|
||||||
// @ts-ignore
|
|
||||||
class MyElement extends LocalizeMixin(LitElement) {
|
class MyElement extends LocalizeMixin(LitElement) {
|
||||||
static get localizeNamespaces() {
|
static get localizeNamespaces() {
|
||||||
return [myElementNs, ...super.localizeNamespaces];
|
return [myElementNs, ...super.localizeNamespaces];
|
||||||
|
|
@ -128,7 +126,6 @@ describe('LocalizeMixin', () => {
|
||||||
'my-element': locale => fakeImport(`./my-element/${locale}.js`),
|
'my-element': locale => fakeImport(`./my-element/${locale}.js`),
|
||||||
};
|
};
|
||||||
|
|
||||||
// @ts-ignore
|
|
||||||
class MyOtherElement extends LocalizeMixin(LitElement) {
|
class MyOtherElement extends LocalizeMixin(LitElement) {
|
||||||
static get localizeNamespaces() {
|
static get localizeNamespaces() {
|
||||||
return [myElementNs, ...super.localizeNamespaces];
|
return [myElementNs, ...super.localizeNamespaces];
|
||||||
|
|
@ -155,17 +152,55 @@ describe('LocalizeMixin', () => {
|
||||||
await localize.loadingComplete;
|
await localize.loadingComplete;
|
||||||
expect(onLocaleChangedSpy.callCount).to.equal(1);
|
expect(onLocaleChangedSpy.callCount).to.equal(1);
|
||||||
// FIXME: Expected 0 arguments, but got 2. ts(2554) --> not sure why this sinon type is not working
|
// FIXME: Expected 0 arguments, but got 2. ts(2554) --> not sure why this sinon type is not working
|
||||||
// @ts-ignore
|
// @ts-expect-error
|
||||||
expect(onLocaleChangedSpy.calledWithExactly('ru-RU', 'nl-NL')).to.be.true;
|
expect(onLocaleChangedSpy.calledWithExactly('ru-RU', 'nl-NL')).to.be.true;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('dispatches "localeChanged" after loader promises have resolved, allowing to call .msg() immediately', async () => {
|
||||||
|
const myElementNs = {
|
||||||
|
/** @param {string} locale */
|
||||||
|
'my-element': locale => fakeImport(`./my-element/${locale}.js`),
|
||||||
|
};
|
||||||
|
|
||||||
|
class MyOtherElement extends LocalizeMixin(LitElement) {
|
||||||
|
static get localizeNamespaces() {
|
||||||
|
return [myElementNs, ...super.localizeNamespaces];
|
||||||
|
}
|
||||||
|
|
||||||
|
onLocaleChanged() {
|
||||||
|
// Can call localize.msg immediately, without having to await localize.loadingComplete
|
||||||
|
// This is because localeChanged event is fired only after awaiting loading
|
||||||
|
// unless the user disables _autoLoadOnLocaleChange property
|
||||||
|
this.foo = localize.msg('my-element:foo');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const tagString = defineCE(MyOtherElement);
|
||||||
|
|
||||||
|
setupEmptyFakeImportsFor(['my-element'], ['en-GB', 'nl-NL', 'ru-RU']);
|
||||||
|
|
||||||
|
const el = /** @type {MyOtherElement} */ (document.createElement(tagString));
|
||||||
|
const wrapper = await fixture('<div></div>');
|
||||||
|
wrapper.appendChild(el);
|
||||||
|
|
||||||
|
await aTimeout(500);
|
||||||
|
|
||||||
|
localize.locale = 'nl-NL';
|
||||||
|
await localize.loadingComplete;
|
||||||
|
expect(el.foo).to.equal('bar-nl-NL');
|
||||||
|
|
||||||
|
localize.locale = 'ru-RU';
|
||||||
|
await localize.loadingComplete;
|
||||||
|
|
||||||
|
expect(el.foo).to.equal('bar-ru-RU');
|
||||||
|
});
|
||||||
|
|
||||||
it('calls "onLocaleUpdated()" after both "onLocaleReady()" and "onLocaleChanged()"', async () => {
|
it('calls "onLocaleUpdated()" after both "onLocaleReady()" and "onLocaleChanged()"', async () => {
|
||||||
const myElementNs = {
|
const myElementNs = {
|
||||||
/** @param {string} locale */
|
/** @param {string} locale */
|
||||||
'my-element': locale => fakeImport(`./my-element/${locale}.js`),
|
'my-element': locale => fakeImport(`./my-element/${locale}.js`),
|
||||||
};
|
};
|
||||||
|
|
||||||
// @ts-ignore
|
|
||||||
class MyElement extends LocalizeMixin(LitElement) {
|
class MyElement extends LocalizeMixin(LitElement) {
|
||||||
static get localizeNamespaces() {
|
static get localizeNamespaces() {
|
||||||
return [myElementNs, ...super.localizeNamespaces];
|
return [myElementNs, ...super.localizeNamespaces];
|
||||||
|
|
@ -187,6 +222,7 @@ describe('LocalizeMixin', () => {
|
||||||
expect(onLocaleUpdatedSpy.callCount).to.equal(1);
|
expect(onLocaleUpdatedSpy.callCount).to.equal(1);
|
||||||
|
|
||||||
localize.locale = 'nl-NL';
|
localize.locale = 'nl-NL';
|
||||||
|
await localize.loadingComplete;
|
||||||
expect(onLocaleUpdatedSpy.callCount).to.equal(2);
|
expect(onLocaleUpdatedSpy.callCount).to.equal(2);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -206,7 +242,6 @@ describe('LocalizeMixin', () => {
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
// @ts-ignore
|
|
||||||
class MyElement extends LocalizeMixin(LitElement) {
|
class MyElement extends LocalizeMixin(LitElement) {
|
||||||
static get localizeNamespaces() {
|
static get localizeNamespaces() {
|
||||||
return [myElementNs, ...super.localizeNamespaces];
|
return [myElementNs, ...super.localizeNamespaces];
|
||||||
|
|
@ -214,7 +249,6 @@ describe('LocalizeMixin', () => {
|
||||||
|
|
||||||
async onLocaleUpdated() {
|
async onLocaleUpdated() {
|
||||||
super.onLocaleUpdated();
|
super.onLocaleUpdated();
|
||||||
await this.localizeNamespacesLoaded;
|
|
||||||
this.label = localize.msg('my-element:label');
|
this.label = localize.msg('my-element:label');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -223,12 +257,11 @@ describe('LocalizeMixin', () => {
|
||||||
const el = /** @type {MyElement} */ (document.createElement(tagString));
|
const el = /** @type {MyElement} */ (document.createElement(tagString));
|
||||||
el.connectedCallback();
|
el.connectedCallback();
|
||||||
|
|
||||||
await el.localizeNamespacesLoaded;
|
|
||||||
await nextFrame(); // needed as both are added to the micro task que
|
await nextFrame(); // needed as both are added to the micro task que
|
||||||
expect(el.label).to.equal('one');
|
expect(el.label).to.equal('one');
|
||||||
|
|
||||||
localize.locale = 'nl-NL';
|
localize.locale = 'nl-NL';
|
||||||
await el.localizeNamespacesLoaded;
|
await localize.loadingComplete;
|
||||||
expect(el.label).to.equal('two');
|
expect(el.label).to.equal('two');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -238,7 +271,6 @@ describe('LocalizeMixin', () => {
|
||||||
'my-element': locale => fakeImport(`./my-element/${locale}.js`),
|
'my-element': locale => fakeImport(`./my-element/${locale}.js`),
|
||||||
};
|
};
|
||||||
|
|
||||||
// @ts-ignore
|
|
||||||
class MyElement extends LocalizeMixin(LitElement) {
|
class MyElement extends LocalizeMixin(LitElement) {
|
||||||
static get localizeNamespaces() {
|
static get localizeNamespaces() {
|
||||||
return [myElementNs, ...super.localizeNamespaces];
|
return [myElementNs, ...super.localizeNamespaces];
|
||||||
|
|
@ -255,6 +287,7 @@ describe('LocalizeMixin', () => {
|
||||||
await el.localizeNamespacesLoaded;
|
await el.localizeNamespacesLoaded;
|
||||||
|
|
||||||
localize.locale = 'nl-NL';
|
localize.locale = 'nl-NL';
|
||||||
|
await localize.loadingComplete;
|
||||||
expect(updateSpy.callCount).to.equal(1);
|
expect(updateSpy.callCount).to.equal(1);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -269,7 +302,6 @@ describe('LocalizeMixin', () => {
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
// @ts-ignore
|
|
||||||
class MyElement extends LocalizeMixin(LitElement) {
|
class MyElement extends LocalizeMixin(LitElement) {
|
||||||
static get localizeNamespaces() {
|
static get localizeNamespaces() {
|
||||||
return [myElementNs, ...super.localizeNamespaces];
|
return [myElementNs, ...super.localizeNamespaces];
|
||||||
|
|
@ -311,7 +343,6 @@ describe('LocalizeMixin', () => {
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
// @ts-ignore
|
|
||||||
class MyElement extends LocalizeMixin(LitElement) {
|
class MyElement extends LocalizeMixin(LitElement) {
|
||||||
static get localizeNamespaces() {
|
static get localizeNamespaces() {
|
||||||
return [myElementNs, ...super.localizeNamespaces];
|
return [myElementNs, ...super.localizeNamespaces];
|
||||||
|
|
@ -338,7 +369,6 @@ describe('LocalizeMixin', () => {
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
// @ts-ignore
|
|
||||||
class MyLocalizedClass extends LocalizeMixin(LitElement) {
|
class MyLocalizedClass extends LocalizeMixin(LitElement) {
|
||||||
static get localizeNamespaces() {
|
static get localizeNamespaces() {
|
||||||
return [myElementNs, ...super.localizeNamespaces];
|
return [myElementNs, ...super.localizeNamespaces];
|
||||||
|
|
@ -379,7 +409,6 @@ describe('LocalizeMixin', () => {
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
// @ts-ignore
|
|
||||||
class MyLocalizedClass extends LocalizeMixin(LitElement) {
|
class MyLocalizedClass extends LocalizeMixin(LitElement) {
|
||||||
static get localizeNamespaces() {
|
static get localizeNamespaces() {
|
||||||
return [myElementNs, ...super.localizeNamespaces];
|
return [myElementNs, ...super.localizeNamespaces];
|
||||||
|
|
@ -400,6 +429,7 @@ describe('LocalizeMixin', () => {
|
||||||
expect(p.innerText).to.equal('Hi!');
|
expect(p.innerText).to.equal('Hi!');
|
||||||
localize.locale = 'en-US';
|
localize.locale = 'en-US';
|
||||||
expect(p.innerText).to.equal('Hi!');
|
expect(p.innerText).to.equal('Hi!');
|
||||||
|
await localize.loadingComplete;
|
||||||
await el.updateComplete;
|
await el.updateComplete;
|
||||||
expect(p.innerText).to.equal('Howdy!');
|
expect(p.innerText).to.equal('Howdy!');
|
||||||
}
|
}
|
||||||
|
|
@ -416,7 +446,6 @@ describe('LocalizeMixin', () => {
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
// @ts-ignore
|
|
||||||
class MyLocalizedClass extends LocalizeMixin(LitElement) {
|
class MyLocalizedClass extends LocalizeMixin(LitElement) {
|
||||||
static get waitForLocalizeNamespaces() {
|
static get waitForLocalizeNamespaces() {
|
||||||
return false;
|
return false;
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue