Skip to content

Commit

Permalink
chore: add src and dest translation
Browse files Browse the repository at this point in the history
  • Loading branch information
leangseu-edx committed Mar 6, 2024
1 parent a842f82 commit 69aa9a3
Show file tree
Hide file tree
Showing 9 changed files with 72 additions and 39 deletions.
1 change: 1 addition & 0 deletions src/course-home/data/api.js
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,7 @@ function normalizeCourseHomeCourseMetadata(metadata, rootSlug) {
url: tab.url,
})),
isMasquerading: data.originalUserIsStaff && !data.isStaff,
language: data.language?.slice(0, 2),
};
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ exports[`Unit component output BookmarkButton props not bookmarked, bookmark upd
/>
`;

exports[`Unit component output TranslationSelection renders if wholeCourseTranslationEnabled 1`] = `
exports[`Unit component output TranslationSelection renders if wholeCourseTranslationEnabled and language is defined 1`] = `
<div
className="unit"
>
Expand Down
14 changes: 9 additions & 5 deletions src/courseware/course/sequence/Unit/index.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -24,24 +24,28 @@ const Unit = ({
const examAccess = useExamAccess({ id });
const shouldDisplayHonorCode = useShouldDisplayHonorCode({ courseId, id });
const unit = useModel(modelKeys.units, id);
const { wholeCourseTranslationEnabled } = useModel('coursewareMeta', courseId);
const { language, wholeCourseTranslationEnabled } = useModel('courseHomeMeta', courseId);
const isProcessing = unit.bookmarkedUpdateState === 'loading';
const view = authenticatedUser ? views.student : views.public;
const { selectedLanguage, setSelectedLanguage } = useSelectLanguage(courseId);
const { selectedLanguage, setSelectedLanguage } = useSelectLanguage({
courseId,
language,
});

const iframeUrl = useMemo(() => getIFrameUrl({
id,
view,
format,
examAccess,
translateLanguage: selectedLanguage,
}), [id, view, format, examAccess, selectedLanguage]);
srcLanguage: language,
destLanguage: selectedLanguage,
}), [id, view, format, examAccess, selectedLanguage, language]);

return (
<div className="unit">
<div className="mb-0">
<h3 className="h3">{unit.title}</h3>
{wholeCourseTranslationEnabled && (
{wholeCourseTranslationEnabled && language && (
<TranslationSelection
courseId={courseId}
selectedLanguage={selectedLanguage}
Expand Down
11 changes: 8 additions & 3 deletions src/courseware/course/sequence/Unit/index.test.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ const mockCoursewareMetaFn = jest.fn(() => ({ wholeCourseTranslationEnabled: fal
const mockUnitsFn = jest.fn(() => unit);

when(useModel)
.calledWith('coursewareMeta', props.courseId)
.calledWith('courseHomeMeta', props.courseId)
.mockImplementation(mockCoursewareMetaFn)
.calledWith(modelKeys.units, props.id)
.mockImplementation(mockUnitsFn);
Expand Down Expand Up @@ -201,8 +201,8 @@ describe('Unit component', () => {
});
});
describe('TranslationSelection', () => {
test('renders if wholeCourseTranslationEnabled', () => {
mockCoursewareMetaFn.mockReturnValueOnce({ wholeCourseTranslationEnabled: true });
test('renders if wholeCourseTranslationEnabled and language is defined', () => {
mockCoursewareMetaFn.mockReturnValueOnce({ wholeCourseTranslationEnabled: true, language: 'en' });
el = shallow(<Unit {...props} />);
expect(el.snapshot).toMatchSnapshot();
expect(el.instance.findByType('TranslationSelection')).toHaveLength(1);
Expand All @@ -212,6 +212,11 @@ describe('Unit component', () => {
el = shallow(<Unit {...props} />);
expect(el.instance.findByType('TranslationSelection')).toHaveLength(0);
});
test('does not render if language is undefined', () => {
mockCoursewareMetaFn.mockReturnValueOnce({ wholeCourseTranslationEnabled: true });
el = shallow(<Unit {...props} />);
expect(el.instance.findByType('TranslationSelection')).toHaveLength(0);
});
});
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import {
} from '../../../../../data/localStorage';

export const selectedLanguageKey = 'selectedLanguages';
// This will be use to check supported languages
export const languages = Object.entries({
en: 'English',
es: 'Spanish',
Expand All @@ -18,11 +19,11 @@ export const stateKeys = StrictDict({
selectedLanguage: 'selectedLanguage',
});

const useSelectLanguage = (courseId) => {
const useSelectLanguage = ({ courseId, language }) => {
const selectedLanguageItem = getLocalStorage(selectedLanguageKey) || {};
const [selectedLanguage, updateSelectedLanguage] = useKeyedState(
stateKeys.selectedLanguage,
selectedLanguageItem[courseId] || 'en',
selectedLanguageItem[courseId] || language,
);

const setSelectedLanguage = useCallback((newSelectedLanguage) => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,10 @@ jest.mock('../../../../../data/localStorage', () => ({
}));

describe('useSelectLanguage', () => {
const courseId = 'some-course-id';
const props = {
courseId: 'test-course-id',
language: 'en',
};

beforeEach(() => {
jest.clearAllMocks();
Expand All @@ -41,21 +44,21 @@ describe('useSelectLanguage', () => {

languages.forEach(([key, value]) => {
it(`initializes selectedLanguage to the selected language (${value})`, () => {
getLocalStorage.mockReturnValueOnce({ [courseId]: key });
const { selectedLanguage } = useSelectLanguage(courseId);
getLocalStorage.mockReturnValueOnce({ [props.courseId]: key });
const { selectedLanguage } = useSelectLanguage(props);

state.expectInitializedWith(stateKeys.selectedLanguage, key);
expect(selectedLanguage).toBe(key);
});
});

test('setSelectedLanguage behavior', () => {
const { setSelectedLanguage } = useSelectLanguage(courseId);
const { setSelectedLanguage } = useSelectLanguage(props);

setSelectedLanguage('es');
state.expectSetStateCalledWith(stateKeys.selectedLanguage, 'es');
expect(setLocalStorage).toHaveBeenCalledWith(selectedLanguageKey, {
[courseId]: 'es',
[props.courseId]: 'es',
});
});
});
Expand Down
8 changes: 6 additions & 2 deletions src/courseware/course/sequence/Unit/urls.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,15 +12,19 @@ export const getIFrameUrl = ({
view,
format,
examAccess,
translateLanguage,
srcLanguage,
destLanguage,
}) => {
const xblockUrl = `${getConfig().LMS_BASE_URL}/xblock/${id}`;
const params = stringify({
...iframeParams,
view,
...(format && { format }),
...(!examAccess.blockAccess && { exam_access: examAccess.accessToken }),
...(translateLanguage && { translate_lang: translateLanguage }),
...(srcLanguage && destLanguage && srcLanguage !== destLanguage && {
src_lang: srcLanguage,
dest_lang: destLanguage,
}),
});
return `${xblockUrl}?${params}`;
};
Expand Down
55 changes: 36 additions & 19 deletions src/courseware/course/sequence/Unit/urls.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,28 +17,45 @@ const props = {
view: 'test-view',
format: 'test-format',
examAccess: { blockAccess: false, accessToken: 'test-access-token' },
translateLanguage: 'test-translate-language',
};

describe('urls module', () => {
describe('getIFrameUrl', () => {
test('format provided, exam access and token available', () => {
const params = stringify({
...iframeParams,
view: props.view,
format: props.format,
exam_access: props.examAccess.accessToken,
translate_lang: props.translateLanguage,
});
expect(getIFrameUrl(props)).toEqual(`${config.LMS_BASE_URL}/xblock/${props.id}?${params}`);
describe('urls module getIFrameUrl', () => {
test('format provided, exam access and token available', () => {
const params = stringify({
...iframeParams,
view: props.view,
format: props.format,
exam_access: props.examAccess.accessToken,
});
test('no format provided, exam access blocked', () => {
const params = stringify({ ...iframeParams, view: props.view });
expect(getIFrameUrl({
id: props.id,
view: props.view,
examAccess: { blockAccess: true },
})).toEqual(`${config.LMS_BASE_URL}/xblock/${props.id}?${params}`);
expect(getIFrameUrl(props)).toEqual(`${config.LMS_BASE_URL}/xblock/${props.id}?${params}`);
});
test('no format provided, exam access blocked', () => {
const params = stringify({ ...iframeParams, view: props.view });
expect(getIFrameUrl({
id: props.id,
view: props.view,
examAccess: { blockAccess: true },
})).toEqual(`${config.LMS_BASE_URL}/xblock/${props.id}?${params}`);
});
test('src and dest languages provided', () => {
const params = stringify({
...iframeParams,
view: props.view,
src_lang: 'test-src-lang',
dest_lang: 'test-dest-lang',
});
expect(getIFrameUrl({
...props,
srcLanguage: 'test-src-lang',
destLanguage: 'test-dest-lang',
})).toEqual(`${config.LMS_BASE_URL}/xblock/${props.id}?${params}`);
});
test('src and dest languages provided are the same', () => {
const params = stringify({ ...iframeParams, view: props.view });
expect(getIFrameUrl({
...props,
srcLanguage: 'test-lang',
destLanguage: 'test-lang',
})).toEqual(`${config.LMS_BASE_URL}/xblock/${props.id}?${params}`);
});
});
2 changes: 0 additions & 2 deletions src/courseware/data/api.js
Original file line number Diff line number Diff line change
Expand Up @@ -110,8 +110,6 @@ function normalizeMetadata(metadata) {
showCalculator: data.show_calculator,
notes: camelCaseObject(data.notes),
marketingUrl: data.marketing_url,
language: data.language,
wholeCourseTranslationEnabled: data.whole_course_translation_enabled,
celebrations: camelCaseObject(data.celebrations),
userHasPassingGrade: data.user_has_passing_grade,
courseExitPageIsActive: data.course_exit_page_is_active,
Expand Down

0 comments on commit 69aa9a3

Please sign in to comment.