From 404df1ee7a6e71be566649cfe04296175e851b76 Mon Sep 17 00:00:00 2001 From: Anuradha Lenka Date: Tue, 21 Apr 2026 06:49:18 +0000 Subject: [PATCH 1/3] fix: integrate RTL handling on locale changes (AU-2863) --- src/index.jsx | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/index.jsx b/src/index.jsx index 8e030ad487..ca1e1c1eaa 100755 --- a/src/index.jsx +++ b/src/index.jsx @@ -3,6 +3,7 @@ import { mergeConfig, getConfig, } from '@edx/frontend-platform'; +import { handleRtl, LOCALE_CHANGED } from '@edx/frontend-platform/i18n'; import { AppProvider, ErrorPage, PageWrap } from '@edx/frontend-platform/react'; import { StrictMode } from 'react'; import { createRoot } from 'react-dom/client'; @@ -37,7 +38,12 @@ import { DECODE_ROUTES, ROUTES } from './constants'; import PreferencesUnsubscribe from './preferences-unsubscribe'; import PageNotFound from './generic/PageNotFound'; +subscribe(LOCALE_CHANGED, () => { + handleRtl(); +}); + subscribe(APP_READY, () => { + handleRtl(); const root = createRoot(document.getElementById('root')); root.render( From 76bf20fb5b528865666501cb3ad538f121d3ca2a Mon Sep 17 00:00:00 2001 From: Anuradha Lenka Date: Tue, 21 Apr 2026 07:28:10 +0000 Subject: [PATCH 2/3] fix: handle RTL in init error + test updates --- .../sequence-navigation/UnitButton.test.jsx | 9 +++-- src/index.jsx | 1 + src/index.test.jsx | 37 ++++++++++++++++--- 3 files changed, 38 insertions(+), 9 deletions(-) diff --git a/src/courseware/course/sequence/sequence-navigation/UnitButton.test.jsx b/src/courseware/course/sequence/sequence-navigation/UnitButton.test.jsx index 7a1fdd8b87..6c1d945463 100644 --- a/src/courseware/course/sequence/sequence-navigation/UnitButton.test.jsx +++ b/src/courseware/course/sequence/sequence-navigation/UnitButton.test.jsx @@ -42,7 +42,7 @@ describe('Unit Button', () => { }); it('does not show completion for non-completed unit', () => { - const { container } = render(); + const { container } = render(, { wrapWithRouter: true }); container.querySelectorAll('svg').forEach(icon => { expect(icon).not.toHaveClass('fa-check'); }); @@ -56,14 +56,17 @@ describe('Unit Button', () => { }); it('hides completion', () => { - const { container } = render(); + const { container } = render( + , + { wrapWithRouter: true }, + ); container.querySelectorAll('svg').forEach(icon => { expect(icon).not.toHaveClass('fa-check'); }); }); it('does not show bookmark', () => { - const { queryByTestId } = render(); + const { queryByTestId } = render(, { wrapWithRouter: true }); expect(queryByTestId('bookmark-icon')).toBeNull(); }); diff --git a/src/index.jsx b/src/index.jsx index ca1e1c1eaa..aecd837224 100755 --- a/src/index.jsx +++ b/src/index.jsx @@ -160,6 +160,7 @@ subscribe(APP_READY, () => { }); subscribe(APP_INIT_ERROR, (error) => { + handleRtl(); const root = createRoot(document.getElementById('root')); root.render( diff --git a/src/index.test.jsx b/src/index.test.jsx index 4ff17b46ff..eacbf50018 100644 --- a/src/index.test.jsx +++ b/src/index.test.jsx @@ -1,6 +1,7 @@ import { APP_INIT_ERROR, APP_READY, subscribe, } from '@edx/frontend-platform'; +import { LOCALE_CHANGED } from '@edx/frontend-platform/i18n'; // Jest needs this for module resolution import * as app from '.'; // eslint-disable-line @typescript-eslint/no-unused-vars @@ -9,6 +10,7 @@ import * as app from '.'; // eslint-disable-line @typescript-eslint/no-unused-va // and can be used by jest.mock (which is also hoisted) var mockRender; // eslint-disable-line no-var var mockCreateRoot; // eslint-disable-line no-var +var mockHandleRtl; // eslint-disable-line no-var jest.mock('react-dom/client', () => { mockRender = jest.fn(); mockCreateRoot = jest.fn(() => ({ @@ -20,6 +22,14 @@ jest.mock('react-dom/client', () => { }); }); +jest.mock('@edx/frontend-platform/i18n', () => { + mockHandleRtl = jest.fn(); + return { + LOCALE_CHANGED: 'LOCALE.CHANGED', + handleRtl: mockHandleRtl, + }; +}); + jest.mock('react', () => ({ ...jest.requireActual('react'), StrictMode: 'React Strict Mode', @@ -54,9 +64,16 @@ jest.mock('./courseware', () => 'Courseware Container'); describe('app registry', () => { let getElement; + const getSubscriptionCallback = (eventKey) => { + const callArgs = subscribe.mock.calls.find(([event]) => event === eventKey); + expect(callArgs).toBeDefined(); + return callArgs[1]; + }; + beforeEach(() => { mockCreateRoot.mockClear(); mockRender.mockClear(); + mockHandleRtl.mockClear(); getElement = window.document.getElementById; window.document.getElementById = jest.fn(id => ({ id })); @@ -65,18 +82,26 @@ describe('app registry', () => { window.document.getElementById = getElement; }); + + test('subscribe: LOCALE_CHANGED. invokes handleRtl', () => { + const callback = getSubscriptionCallback(LOCALE_CHANGED); + callback(); + expect(mockHandleRtl).toHaveBeenCalledTimes(1); + }); + test('subscribe: APP_READY. links App to root element', () => { - const callArgs = subscribe.mock.calls[0]; - expect(callArgs[0]).toEqual(APP_READY); - callArgs[1](); + const callback = getSubscriptionCallback(APP_READY); + callback(); + expect(mockHandleRtl).toHaveBeenCalledTimes(1); const [rendered] = mockRender.mock.calls[0]; expect(rendered).toMatchSnapshot(); }); + test('subscribe: APP_INIT_ERROR. snapshot: displays an ErrorPage to root element', () => { - const callArgs = subscribe.mock.calls[1]; - expect(callArgs[0]).toEqual(APP_INIT_ERROR); + const callback = getSubscriptionCallback(APP_INIT_ERROR); const error = { message: 'test-error-message' }; - callArgs[1](error); + callback(error); + expect(mockHandleRtl).toHaveBeenCalledTimes(1); const [rendered] = mockRender.mock.calls[0]; expect(rendered).toMatchSnapshot(); }); From 7f1c11216d8eed442004a35fab68dec52ab07e1e Mon Sep 17 00:00:00 2001 From: Anuradha Lenka Date: Tue, 21 Apr 2026 07:46:14 +0000 Subject: [PATCH 3/3] chore: fix lint spacing in index test --- src/index.test.jsx | 1 - 1 file changed, 1 deletion(-) diff --git a/src/index.test.jsx b/src/index.test.jsx index eacbf50018..c5d257adc1 100644 --- a/src/index.test.jsx +++ b/src/index.test.jsx @@ -82,7 +82,6 @@ describe('app registry', () => { window.document.getElementById = getElement; }); - test('subscribe: LOCALE_CHANGED. invokes handleRtl', () => { const callback = getSubscriptionCallback(LOCALE_CHANGED); callback();