Skip to content

Commit b6492f7

Browse files
🐛 Fixed data-locale being ignored in Portal (#25190)
no issue - when we updated `data-attributes.js` to use the importable `t` function that uses a single instance of the `i18n` library we inadvertently introduced an override of the locale so it always matches the site locale despite using a `data-locale=` attribute in Portal's `<script>` tag - ultimately this uncovered a long-standing bug from before the switch to importable `t` where custom forms would always use the site locale rather than a data-attribute override - removed the now-unnecessary language change calls along with any tests for them - added test to ensure all calls to `changeLanguage` that occur during App startup use the prop rather than site locale
1 parent baff818 commit b6492f7

File tree

3 files changed

+57
-46
lines changed

3 files changed

+57
-46
lines changed

apps/portal/src/data-attributes.js

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
/* eslint-disable no-console */
22
import {getCheckoutSessionDataFromPlanAttribute, getUrlHistory} from './utils/helpers';
33
import {HumanReadableError, chooseBestErrorMessage} from './utils/errors';
4-
import i18n, {t} from './utils/i18n';
4+
import {t} from './utils/i18n';
55

66
function displayErrorIfElementExists(errorEl, message) {
77
if (errorEl) {
@@ -125,8 +125,6 @@ export async function formSubmitHandler(
125125
}
126126

127127
export function planClickHandler({event, el, errorEl, siteUrl, site, member, clickHandler}) {
128-
i18n.changeLanguage(site.locale || 'en');
129-
130128
el.removeEventListener('click', clickHandler);
131129
event.preventDefault();
132130
let plan = el.dataset.membersPlan;
@@ -211,8 +209,6 @@ export function handleDataAttributes({siteUrl, site = {}, member, labs = {}, doA
211209
return;
212210
}
213211

214-
i18n.changeLanguage(site.locale || 'en');
215-
216212
siteUrl = siteUrl.replace(/\/$/, '');
217213
Array.prototype.forEach.call(document.querySelectorAll('form[data-members-form]'), function (form) {
218214
let errorEl = form.querySelector('[data-members-error]');

apps/portal/src/tests/App.test.js

Lines changed: 55 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,17 @@ import App from '../App';
22
import setupGhostApi from '../utils/api';
33
import {appRender} from '../utils/test-utils';
44
import {site as FixtureSite, member as FixtureMember} from '../utils/test-fixtures';
5+
import i18n from '../utils/i18n';
6+
import {vi} from 'vitest';
7+
8+
vi.mock('../utils/i18n', () => ({
9+
default: {
10+
changeLanguage: vi.fn(),
11+
dir: vi.fn(),
12+
t: vi.fn(str => str)
13+
},
14+
t: vi.fn(str => str)
15+
}));
516

617
describe('App', function () {
718
beforeEach(function () {
@@ -11,24 +22,61 @@ describe('App', function () {
1122
window.location = location;
1223
});
1324

14-
test('transforms portal links on render', async () => {
15-
const link = document.createElement('a');
16-
link.setAttribute('href', 'http://example.com/#/portal/signup');
17-
document.body.appendChild(link);
25+
function setupApi({site = {}, member = {}} = {}) {
26+
const defaultSite = FixtureSite.singleTier.basic;
27+
const defaultMember = FixtureMember.free;
28+
29+
const siteFixtures = {
30+
...defaultSite,
31+
...site
32+
};
33+
34+
const memberFixtures = {
35+
...defaultMember,
36+
...member
37+
};
1838

1939
const ghostApi = setupGhostApi({siteUrl: 'http://example.com'});
2040
ghostApi.init = vi.fn(() => {
2141
return Promise.resolve({
22-
site: FixtureSite.singleTier.basic,
23-
member: FixtureMember.free
42+
site: siteFixtures,
43+
member: memberFixtures
2444
});
2545
});
46+
47+
return ghostApi;
48+
}
49+
50+
test('transforms portal links on render', async () => {
51+
const link = document.createElement('a');
52+
link.setAttribute('href', 'http://example.com/#/portal/signup');
53+
document.body.appendChild(link);
54+
55+
const ghostApi = setupApi();
2656
const utils = appRender(
27-
<App api={ghostApi} />
57+
<App siteUrl="http://example.com" api={ghostApi} />
2858
);
2959

3060
await utils.findByTitle(/portal-popup/i);
3161

3262
expect(link.getAttribute('href')).toBe('#/portal/signup');
3363
});
64+
65+
test('prefers locale prop over site locale for i18n language', async () => {
66+
const ghostApi = setupApi({
67+
site: {
68+
locale: 'de'
69+
}
70+
});
71+
72+
const utils = appRender(
73+
<App siteUrl="http://example.com" api={ghostApi} locale="en" />
74+
);
75+
76+
await utils.findByTitle(/portal-popup/i);
77+
78+
i18n.changeLanguage.mock.calls.forEach((call) => {
79+
expect(call[0]).toBe('en');
80+
});
81+
});
3482
});

apps/portal/src/tests/data-attributes.test.js

Lines changed: 1 addition & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -3,19 +3,9 @@ import {site as FixturesSite, member as FixtureMember} from '../utils/test-fixtu
33
import {fireEvent, appRender, within} from '../utils/test-utils';
44
import setupGhostApi from '../utils/api';
55
import * as helpers from '../utils/helpers';
6-
import {formSubmitHandler, planClickHandler, handleDataAttributes} from '../data-attributes';
7-
import i18n from '../utils/i18n';
6+
import {formSubmitHandler, planClickHandler} from '../data-attributes';
87
import {vi} from 'vitest';
98

10-
vi.mock('../utils/i18n', () => ({
11-
default: {
12-
changeLanguage: vi.fn(),
13-
dir: vi.fn(),
14-
t: vi.fn(str => str)
15-
},
16-
t: vi.fn(str => str)
17-
}));
18-
199
// Mock data
2010
function getMockData({newsletterQuerySelectorResult = null} = {}) {
2111
const site = FixturesSite.singleTier.basic;
@@ -322,17 +312,6 @@ describe('Member Data attributes:', () => {
322312
);
323313
});
324314

325-
test('sets correct i18n language based on site locale', async () => {
326-
const {event, errorEl, siteUrl, clickHandler, site, member, element} = getMockData();
327-
// Override the site locale to 'de'
328-
site.locale = 'de';
329-
330-
await planClickHandler({event, errorEl, siteUrl, clickHandler, site, member, el: element});
331-
332-
// Verify i18nLib was called with correct locale
333-
expect(i18n.changeLanguage).toHaveBeenCalledWith('de');
334-
});
335-
336315
test('allows free member upgrade via direct checkout', async () => {
337316
let {event, errorEl, siteUrl, clickHandler, site, member, element} = getMockData();
338317
member = FixtureMember.free;
@@ -718,16 +697,4 @@ describe('Portal Data attributes:', () => {
718697
expect(errorEl.innerText).toBe('No member exists with this e-mail address. Please sign up first.');
719698
});
720699
});
721-
722-
describe('handleDataAttributes', () => {
723-
test('sets correct i18n language based on site locale', () => {
724-
const {siteUrl, site, member} = getMockData();
725-
// Override the site locale to 'de'
726-
site.locale = 'de';
727-
728-
handleDataAttributes({siteUrl, site, member});
729-
730-
expect(i18n.changeLanguage).toHaveBeenCalledWith('de');
731-
});
732-
});
733700
});

0 commit comments

Comments
 (0)