Skip to content

Commit 5c14375

Browse files
fix: use camel case response
1 parent e3d26ad commit 5c14375

File tree

5 files changed

+22
-15
lines changed

5 files changed

+22
-15
lines changed

jest.config.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@ module.exports = createConfig('test', {
1010
'src/__mocks__',
1111
],
1212
moduleNameMapper: {
13+
// This alias is for any code in the src directory that wants to avoid '../../' style relative imports:
14+
'^@src/(.*)$': '<rootDir>/src/$1',
1315
'\\.svg$': '<rootDir>/src/__mocks__/svg.js',
1416
'\\.(jpg|jpeg|png|gif|eot|otf|webp|ttf|woff|woff2|mp4|webm|wav|mp3|m4a|aac|oga)$': '<rootDir>/src/__mocks__/file.js',
1517
},

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@
1818
"*.scss"
1919
],
2020
"scripts": {
21-
"dev": "PORT=8081 PUBLIC_PATH=/instructor openedx dev",
21+
"dev": "PORT=8080 PUBLIC_PATH=/instructor openedx dev",
2222
"i18n_extract": "openedx formatjs extract",
2323
"lint": "openedx lint .",
2424
"lint:fix": "openedx lint --fix .",

src/data/api.test.ts

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { getCourseInfo } from './api';
2-
import { getAppConfig, getAuthenticatedHttpClient } from '@openedx/frontend-base';
2+
import { getAppConfig, getAuthenticatedHttpClient, camelCaseObject } from '@openedx/frontend-base';
33

44
jest.mock('@openedx/frontend-base');
55

@@ -9,15 +9,18 @@ const mockHttpClient = {
99
};
1010

1111
const mockGetAppConfig = getAppConfig as jest.MockedFunction<typeof getAppConfig>;
12+
const mockCamelCaseObject = camelCaseObject as jest.MockedFunction<typeof camelCaseObject>;
1213
const mockGetAuthenticatedHttpClient = getAuthenticatedHttpClient as jest.MockedFunction<typeof getAuthenticatedHttpClient>;
1314

1415
describe('getCourseInfo', () => {
1516
const mockCourseData = { course_name: 'Test Course', tabs: [{ tab_id: 'course_info', title: 'Course Information', url: 'https://test-lms.com/courses/test-course-123/info' }] };
17+
const mockCamelCasedCourseData = { courseName: 'Test Course', tabs: [{ tabId: 'course_info', title: 'Course Information', url: 'https://test-lms.com/courses/test-course-123/info' }] };
1618

1719
beforeEach(() => {
1820
jest.clearAllMocks();
1921
mockGetAppConfig.mockReturnValue({ LMS_BASE_URL: 'https://test-lms.com' });
2022
mockGetAuthenticatedHttpClient.mockReturnValue(mockHttpClient as any);
23+
mockCamelCaseObject.mockReturnValue(mockCamelCasedCourseData);
2124
mockHttpClient.get.mockResolvedValue({ data: mockCourseData });
2225
});
2326

@@ -27,7 +30,7 @@ describe('getCourseInfo', () => {
2730
expect(mockGetAppConfig).toHaveBeenCalledWith('org.openedx.frontend.app.instructor');
2831
expect(mockGetAuthenticatedHttpClient).toHaveBeenCalled();
2932
expect(mockHttpClient.get).toHaveBeenCalledWith('https://test-lms.com/api/instructor/v2/courses/test-course-123');
30-
expect(result).toBe(mockCourseData);
33+
expect(result).toBe(mockCamelCasedCourseData);
3134
});
3235

3336
it('throws error when API call fails', async () => {

src/instructorTabs/InstructorTabs.tsx

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -5,15 +5,15 @@ import { SlotContext, useWidgetsForId } from '@openedx/frontend-base';
55
import { useCourseInfo } from '../data/apiHook';
66

77
export interface TabProps {
8-
tab_id: string,
8+
tabId: string,
99
url: string,
1010
title: string,
1111
}
1212

1313
const extractWidgetProps = (widget: React.ReactNode): TabProps | null => {
1414
if (widget && typeof widget === 'object' && 'props' in widget) {
1515
const props = widget.props.children.props as TabProps;
16-
if (props?.tab_id && props?.url && props?.title) {
16+
if (props?.tabId && props?.url && props?.title) {
1717
return props;
1818
}
1919
}
@@ -35,11 +35,13 @@ const InstructorTabs = () => {
3535
const apiTabs: TabProps[] = courseInfo?.tabs ?? [];
3636
const allTabs = [...apiTabs];
3737

38+
// Tabs added via slot take priority over (read: replace) tabs from the API
39+
// All tabs added via slot are placed at the end of the tabs array
3840
widgetPropsArray.forEach(slotTab => {
39-
if (!apiTabs.find(apiTab => apiTab.tab_id === slotTab.tab_id)) {
41+
if (!apiTabs.find(apiTab => apiTab.tabId === slotTab.tabId)) {
4042
allTabs.push(slotTab);
4143
} else {
42-
const indexToRemove = allTabs.findIndex(({ tab_id }) => tab_id === slotTab.tab_id);
44+
const indexToRemove = allTabs.findIndex(({ tabId }) => tabId === slotTab.tabId);
4345
if (indexToRemove !== -1) {
4446
allTabs.splice(indexToRemove, 1);
4547
}
@@ -50,7 +52,7 @@ const InstructorTabs = () => {
5052
const activeKey = tabId ?? 'course_info';
5153
const handleSelect = (eventKey: string | null) => {
5254
if (eventKey && courseId) {
53-
const selectedTab = allTabs.find(({ tab_id }) => tab_id === eventKey);
55+
const selectedTab = allTabs.find(({ tabId }) => tabId === eventKey);
5456
if (selectedTab) {
5557
navigate(`/${courseId}/${eventKey}`);
5658
}
@@ -65,8 +67,8 @@ const InstructorTabs = () => {
6567

6668
return (
6769
<Tabs id="instructor-tabs" activeKey={activeKey} onSelect={handleSelect}>
68-
{allTabs.map(({ tab_id, title }) => (
69-
<Tab key={tab_id} eventKey={tab_id} title={title} />
70+
{allTabs.map(({ tabId, title }) => (
71+
<Tab key={tabId} eventKey={tabId} title={title} />
7072
))}
7173
</Tabs>
7274
);

src/slots/README.md

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -21,14 +21,14 @@ import { SlotOperation, WidgetOperationTypes } from '@openedx/frontend-base';
2121
import { InstructorTab } from 'src/slots/instructorTabsSlot/InstructorTabsSlot';
2222

2323
// Tab configuration data
24-
const tabData = { tab_id: 'course_info', url: 'course_info', title: 'Course Info' };
24+
const tabData = { tabId: 'course_info', url: 'course_info', title: 'Course Info' };
2525

2626
// Create slot operations
2727
export const tabSlots: SlotOperation[] = [{
2828
slotId: `org.openedx.frontend.slot.instructor.tabs.v1`,
29-
id: `org.openedx.frontend.widget.instructor.tab.${tab_id}`,
29+
id: `org.openedx.frontend.widget.instructor.tab.${tabId}`,
3030
op: WidgetOperationTypes.APPEND,
31-
element: <InstructorTab tab_id={tabData.tab_id} title={tabData.title} url={tabData.url} />,
31+
element: <InstructorTab tabId={tabData.tabId} title={tabData.title} url={tabData.url} />,
3232
}];
3333
```
3434

@@ -60,8 +60,8 @@ const InstructorTabs = () => {
6060
return (
6161
<Tabs>
6262
{widgets.map((widget, index) => {
63-
const { tab_id, title } = widget.element.props;
64-
return <Tab key={tab_id} eventKey={tab_id} title={title} />;
63+
const { tabId, title } = widget.element.props;
64+
return <Tab key={tabId} eventKey={tabId} title={title} />;
6565
})}
6666
</Tabs>
6767
);

0 commit comments

Comments
 (0)