Skip to content

Commit

Permalink
feat: adding pagination for teams
Browse files Browse the repository at this point in the history
  • Loading branch information
johnvente committed Jan 5, 2024
1 parent 9280702 commit f96e899
Show file tree
Hide file tree
Showing 6 changed files with 55 additions and 29 deletions.
3 changes: 3 additions & 0 deletions plugins/communications-app/ScheduleSection/index.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,7 @@ const ScheduleSection = ({ openTaskAlert }) => {
complete: <Icon src={Check} />,
completeSchedule: <Icon src={Check} />,
error: <Icon src={Cancel} />,
loadingTeams: <Icon src={Send} />,
}), []);

const statefulButtonLabels = useMemo(() => ({
Expand All @@ -99,12 +100,14 @@ const ScheduleSection = ({ openTaskAlert }) => {
complete: intl.formatMessage(messages.ScheduleSectionSubmitButtonComplete),
completeSchedule: intl.formatMessage(messages.ScheduleSectionSubmitButtonCompleteSchedule),
error: intl.formatMessage(messages.ScheduleSectionSubmitButtonError),
loadingTeams: intl.formatMessage(messages.ScheduleSectionSubmitButtonLoadingTeams),
}), [intl]);

const statefulButtonDisableStates = useMemo(() => [
'pending',
'complete',
'completeSchedule',
'loadingTeams',
], []);

return (
Expand Down
4 changes: 4 additions & 0 deletions plugins/communications-app/ScheduleSection/messages.js
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,10 @@ const messages = defineMessages({
id: 'schedule.section.submit.button.schedule.complete',
defaultMessage: 'Scheduling Done',
},
ScheduleSectionSubmitButtonLoadingTeams: {
id: 'schedule.section.submit.button.loading.teams',
defaultMessage: 'Loading teams',
},
});

export default messages;
4 changes: 2 additions & 2 deletions plugins/communications-app/TeamEmails/api.js
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import { getConfig } from '@edx/frontend-platform';
import { getAuthenticatedHttpClient } from '@edx/frontend-platform/auth';

export async function getTeamsList(courseId, page = 1, pageSize = 100) {
export async function getTopicsList(courseId, page = 1, pageSize = 100) {
const endpointUrl = `${
getConfig().LMS_BASE_URL
}/platform-plugin-teams/${courseId}/api/topics/?page=${page}&pageSize=${pageSize}`;
}/platform-plugin-teams/${courseId}/api/topics/?page=${page}&page_size=${pageSize}`;
try {
const response = await getAuthenticatedHttpClient().get(endpointUrl);
return response;
Expand Down
14 changes: 7 additions & 7 deletions plugins/communications-app/TeamEmails/api.test.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { getAuthenticatedHttpClient } from '@edx/frontend-platform/auth';
import { getConfig } from '@edx/frontend-platform';

import { getTeamsList } from './api';
import { getTopicsList } from './api';

jest.mock('@edx/frontend-platform/auth', () => ({
getAuthenticatedHttpClient: jest.fn(),
Expand All @@ -10,7 +10,7 @@ jest.mock('@edx/frontend-platform', () => ({
getConfig: jest.fn(),
}));

describe('getTeamsList function', () => {
describe('getTopicsList function', () => {
const mockCourseId = 'course123';
const mockResponseData = { data: 'someData' };
const mockConfig = { LMS_BASE_URL: 'http://localhost' };
Expand All @@ -23,30 +23,30 @@ describe('getTeamsList function', () => {
});

test('successfully fetches teams list with default parameters', async () => {
const response = await getTeamsList(mockCourseId);
const response = await getTopicsList(mockCourseId);

expect(response).toEqual(mockResponseData);
expect(getAuthenticatedHttpClient().get).toHaveBeenCalledWith(
`http://localhost/platform-plugin-teams/${mockCourseId}/api/topics/?page=1&pageSize=100`,
`http://localhost/platform-plugin-teams/${mockCourseId}/api/topics/?page=1&page_size=100`,
);
});

test('successfully fetches teams list with custom page and pageSize', async () => {
const customPage = 2;
const customPageSize = 50;

const response = await getTeamsList(mockCourseId, customPage, customPageSize);
const response = await getTopicsList(mockCourseId, customPage, customPageSize);

expect(response).toEqual(mockResponseData);
expect(getAuthenticatedHttpClient().get).toHaveBeenCalledWith(
`http://localhost/platform-plugin-teams/${mockCourseId}/api/topics/?page=${customPage}&pageSize=${customPageSize}`,
`http://localhost/platform-plugin-teams/${mockCourseId}/api/topics/?page=${customPage}&page_size=${customPageSize}`,
);
});

test('handles an error', async () => {
const errorMessage = 'Network error';
getAuthenticatedHttpClient().get.mockRejectedValue(new Error(errorMessage));

await expect(getTeamsList(mockCourseId)).rejects.toThrow(errorMessage);
await expect(getTopicsList(mockCourseId)).rejects.toThrow(errorMessage);
});
});
45 changes: 32 additions & 13 deletions plugins/communications-app/TeamEmails/index.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import { actionCreators as formActions } from '@communications-app/src/component

import ListTeams from './ListTeams';
import messages from './messages';
import { getTeamsList } from './api';
import { getTopicsList } from './api';
import { getTeamsFromTopics, convertSnakeCaseToCamelCase } from './utils';

const TeamEmails = ({ courseId }) => {
Expand All @@ -24,25 +24,44 @@ const TeamEmails = ({ courseId }) => {
} = formData;
const [teams, setTeams] = useState([]);
const [checkedTeams, setCheckedTeams] = useState([]);
const [loadingTeams, setLoadingTeams] = useState(false);
const previousFormStatusRef = useRef(null);

useEffect(() => {
const getTeamsFromApi = async () => {
try {
const responseTeams = await getTeamsList(courseId);
const { results } = responseTeams.data;
const camelCaseResult = convertSnakeCaseToCamelCase(results);
const formatResult = getTeamsFromTopics(camelCaseResult);
setTeams(formatResult);
} catch (error) {
console.error('there was an error while getting teams', error.messages);
const fetchTeams = async (page = 1) => {
try {
setLoadingTeams(true);
const responseTopics = await getTopicsList(courseId, page);
const { results, next } = responseTopics.data;

const camelCaseResult = convertSnakeCaseToCamelCase(results);
const formatResult = getTeamsFromTopics(camelCaseResult);

setTeams((prevTeams) => [...prevTeams, ...formatResult]);

if (next) {
fetchTeams(page + 1);
} else {
dispatch(formActions.updateForm({ formStatus: 'default' }));
}
};
} catch (error) {
console.error('There was an error while getting teams:', error.message);

Check warning on line 47 in plugins/communications-app/TeamEmails/index.jsx

View workflow job for this annotation

GitHub Actions / tests

Unexpected console statement
} finally {
setLoadingTeams(false);
}
};

getTeamsFromApi();
useEffect(() => {
fetchTeams();
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [courseId]);

useEffect(() => {
if (loadingTeams) {
dispatch(formActions.updateForm({ formStatus: 'loadingTeams' }));
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [formStatus, loadingTeams]);

useEffect(() => {
if (teams.length) {
dispatch(formActions.updateForm({ teamsListFullData: teams }));
Expand Down
14 changes: 7 additions & 7 deletions plugins/communications-app/TeamEmails/index.test.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -148,7 +148,7 @@ describe('TeamEmails Component', () => {
beforeEach(() => {
jest.clearAllMocks();

api.getTeamsList.mockResolvedValue({ data: mockData });
api.getTopicsList.mockResolvedValue({ data: mockData });
useSelector.mockImplementation((selectorFn) => selectorFn({
form: {
teamsList: [],
Expand All @@ -169,7 +169,7 @@ describe('TeamEmails Component', () => {
);

test('renders the component without errors', async () => {
api.getTeamsList.mockResolvedValue({ data: mockData });
api.getTopicsList.mockResolvedValue({ data: mockData });

render(
<IntlProviderWrapper>
Expand All @@ -189,7 +189,7 @@ describe('TeamEmails Component', () => {
});

test('renders null when teams are empty', async () => {
api.getTeamsList.mockResolvedValue({ data: { results: [] } });
api.getTopicsList.mockResolvedValue({ data: { results: [] } });
render(
<IntlProviderWrapper>
<TeamEmails courseId="course-placeholder-id" />
Expand All @@ -205,7 +205,7 @@ describe('TeamEmails Component', () => {
test('handles checkbox change', async () => {
const mockDispatch = jest.fn();
useDispatch.mockReturnValue(mockDispatch);
api.getTeamsList.mockResolvedValue({ data: mockData });
api.getTopicsList.mockResolvedValue({ data: mockData });

render(
<IntlProviderWrapper>
Expand All @@ -220,9 +220,9 @@ describe('TeamEmails Component', () => {
});
});

test('handles error when api.getTeamsList fails', async () => {
test('handles error when api.getTopicsList fails', async () => {
const mockedError = new Error('API Failed');
api.getTeamsList.mockRejectedValue(mockedError);
api.getTopicsList.mockRejectedValue(mockedError);

const consoleSpy = jest.spyOn(console, 'error').mockImplementation(() => {});

Expand All @@ -233,7 +233,7 @@ describe('TeamEmails Component', () => {
);

await waitFor(() => {
expect(consoleSpy).toHaveBeenCalledWith('there was an error while getting teams', mockedError.messages);
expect(consoleSpy).toHaveBeenCalledWith('There was an error while getting teams:', mockedError.message);
});

consoleSpy.mockRestore();
Expand Down

0 comments on commit f96e899

Please sign in to comment.