Skip to content

Commit

Permalink
Merge pull request #15 from Pearson-Advance/vue/PADV-802
Browse files Browse the repository at this point in the history
PADV-802 Change students table according to new wireframe
  • Loading branch information
AuraAlba authored Nov 16, 2023
2 parents cedb8e0 + e6e845c commit c709744
Show file tree
Hide file tree
Showing 14 changed files with 626 additions and 185 deletions.
1 change: 1 addition & 0 deletions __mocks__/fileMock.js
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
module.exports = 'test-file-stub';
1 change: 1 addition & 0 deletions __mocks__/stylesMock.js
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
module.exports = {};
5 changes: 5 additions & 0 deletions jest.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,4 +9,9 @@ module.exports = createConfig('jest', {
'src/setupTest.js',
'src/i18n',
],
moduleNameMapper: {
'\\.(jpg|jpeg|png|gif|eot|otf|webp|svg|ttf|woff|woff2|mp4|webm|wav|mp3|m4a|aac|oga)$':
'<rootDir>/__mocks__/fileMock.js',
'\\.(css|less)$': '<rootDir>/__mocks__/styleMock.js',
},
});
345 changes: 345 additions & 0 deletions package-lock.json

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@
"react": "16.14.0",
"react-dom": "16.14.0",
"react-intl": "^5.25.1",
"react-paragon-topaz": "^1.0.2",
"react-router": "5.2.1",
"react-router-dom": "5.3.0",
"regenerator-runtime": "0.13.11"
Expand Down
52 changes: 22 additions & 30 deletions src/features/Students/StudentsPage/_test_/index.test.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,6 @@ import axios from 'axios';
import StudentsPage from 'features/Students/StudentsPage';
import {
render,
screen,
fireEvent,
waitFor,
} from '@testing-library/react';
import '@testing-library/jest-dom/extend-expect';
Expand All @@ -21,16 +19,30 @@ const mockResponse = {
{
learnerName: 'Student 1',
learnerEmail: '[email protected]',
ccxName: 'CCX 1',
courseId: '1',
courseName: 'course 1',
classId: '1',
className: 'class 1',
instructors: ['Instructor 1'],
created: 'Fri, 25 Aug 2023 19:01:22 GMT',
firstAccess: 'Fri, 25 Aug 2023 19:01:23 GMT',
lastAccess: 'Fri, 25 Aug 2023 20:20:22 GMT',
status: 'Active',
examReady: true,
},
{
learnerName: 'Student 2',
learnerEmail: '[email protected]',
ccxName: 'CCX 2',
courseId: '2',
courseName: 'course 2',
classId: '2',
className: 'class 2',
instructors: ['Instructor 2'],
created: 'Sat, 26 Aug 2023 19:01:22 GMT',
firstAccess: 'Sat, 26 Aug 2023 19:01:24 GMT',
lastAccess: 'Sat, 26 Aug 2023 21:22:22 GMT',
status: 'Pending',
examReady: null,
},
],
count: 2,
Expand All @@ -48,34 +60,14 @@ describe('StudentsPage', () => {
waitFor(() => {
expect(component.container).toHaveTextContent('Student 1');
expect(component.container).toHaveTextContent('Student 2');
expect(component.container).toHaveTextContent('[email protected]');
expect(component.container).toHaveTextContent('[email protected]');
expect(component.container).toHaveTextContent('CCX 1');
expect(component.container).toHaveTextContent('CCX 2');
expect(component.container).toHaveTextContent('course 1');
expect(component.container).toHaveTextContent('course 2');
expect(component.container).toHaveTextContent('class 1');
expect(component.container).toHaveTextContent('class 2');
expect(component.container).toHaveTextContent('Instructor 1');
expect(component.container).toHaveTextContent('Instructor 2');
expect(component.container).toHaveTextContent('Fri, 25 Aug 2023 19:01:22 GMT');
expect(component.container).toHaveTextContent('Sat, 26 Aug 2023 19:01:22 GMT');
});
});

it('filters students data', async () => {
axios.get.mockResolvedValue(mockResponse);

const component = render(<StudentsPage />);

fireEvent.click(screen.getByRole('button', { name: /Filters/i }));

const nameInput = screen.getByPlaceholderText('Enter Student Name');
const emailInput = screen.getByPlaceholderText('Enter Student Email');
fireEvent.change(nameInput, { target: { value: 'Student 1' } });
fireEvent.change(emailInput, { target: { value: '[email protected]' } });

fireEvent.click(screen.getByText('Apply Filters'));

waitFor(() => {
expect(component.container).toHaveTextContent('Student 1');
expect(screen.queryByText('Student 2')).toBeNull();
expect(component.container).toHaveTextContent('active');
expect(component.container).toHaveTextContent('pending');
});
});
});
99 changes: 99 additions & 0 deletions src/features/Students/StudentsPage/_test_/reducer.test.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
import {
FETCH_STUDENTS_DATA_REQUEST,
FETCH_STUDENTS_DATA_SUCCESS,
FETCH_STUDENTS_DATA_FAILURE,
UPDATE_CURRENT_PAGE,
OPEN_MODAL,
CLOSE_MODAL,
} from 'features/Students/actionTypes';
import { RequestStatus } from 'features/constants';
import reducer from 'features/Instructors/InstructorsPage/reducer';

describe('Instructor page reducers', () => {
const initialState = {
data: [],
error: null,
currentPage: 1,
numPages: 0,
};

test('should handle FETCH_STUDENTS_DATA_REQUEST', () => {
const state = {
...initialState,
status: RequestStatus.LOADING,
};
const action = {
type: FETCH_STUDENTS_DATA_REQUEST,
};
expect(reducer(state, action)).toEqual(state);
});

test('should handle FETCH_STUDENTS_DATA_SUCCESS', () => {
const state = {
...initialState,
status: RequestStatus.SUCCESS,
count: 0,
};
const action = {
type: FETCH_STUDENTS_DATA_SUCCESS,
payload: {
results: [],
count: 0,
numPages: 0,
},
};
expect(reducer(state, action)).toEqual(state);
});

test('should handle FETCH_STUDENTS_DATA_FAILURE', () => {
const state = {
...initialState,
status: RequestStatus.ERROR,
error: '',
};
const action = {
type: FETCH_STUDENTS_DATA_FAILURE,
payload: '',
};
expect(reducer(state, action)).toEqual(state);
});

test('should handle UPDATE_CURRENT_PAGE', () => {
const state = {
...initialState,
currentPage: 1,
};
const action = {
type: UPDATE_CURRENT_PAGE,
payload: 1,
};
expect(reducer(state, action)).toEqual(state);
});

test('should handle OPEN_MODAL', () => {
const state = {
...initialState,
filters: {
isOpenFilters: true,
},
};
const action = {
type: OPEN_MODAL,
};
expect(reducer(state, action)).toEqual(state);
});

test('should handle CLOSE_MODAL', () => {
const state = {
...initialState,
filters: {
isOpenFilters: false,
errors: {},
},
};
const action = {
type: CLOSE_MODAL,
};
expect(reducer(state, action)).toEqual(state);
});
});
68 changes: 15 additions & 53 deletions src/features/Students/StudentsPage/index.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { getStudentbyInstitutionAdmin } from 'features/Students/data/api';
import { StudentsTable } from 'features/Students/StudentsTable/index';
import { StudentsFilters } from 'features/Students/StudentsFilters';
import { RequestStatus } from 'features/constants';
import reducer from 'features/Students/StudentsPage/reducer';

import { logError } from '@edx/frontend-platform/logging';
import Container from '@edx/paragon/dist/Container';
Expand All @@ -20,6 +21,14 @@ import {
Pagination,
Modal,
} from '@edx/paragon';
import {
FETCH_STUDENTS_DATA_REQUEST,
FETCH_STUDENTS_DATA_SUCCESS,
FETCH_STUDENTS_DATA_FAILURE,
UPDATE_CURRENT_PAGE,
OPEN_MODAL,
CLOSE_MODAL,
} from 'features/Students/actionTypes';

const initialFilterFormValues = {
learnerName: '',
Expand All @@ -40,66 +49,19 @@ const initialState = {
},
};

const reducer = (state, action) => {
switch (action.type) {
case 'FETCH_REQUEST':
return { ...state, status: RequestStatus.LOADING };
case 'FETCH_SUCCESS': {
const { results, count, numPages } = action.payload;
return {
...state,
status: RequestStatus.SUCCESS,
data: results,
numPages,
count,
};
}
case 'FETCH_FAILURE':
return {
...state,
status: RequestStatus.ERROR,
error: action.payload,
};
case 'UPDATE_CURRENT_PAGE':
return {
...state,
currentPage: action.payload,
};
case 'OPEN_MODAL':
return {
...state,
filters: {
...state.filters,
isOpenFilters: true,
},
};
case 'CLOSE_MODAL':
return {
...state,
filters: {
...state.filters,
isOpenFilters: false,
errors: {},
},
};
default:
return state;
}
};

const StudentsPage = () => {
const [state, dispatch] = useReducer(reducer, initialState);
const [currentPage, setCurrentPage] = useState(1);
const [filters, setFilters] = useState(initialFilterFormValues);

const fetchData = async () => {
dispatch({ type: 'FETCH_REQUEST' });
dispatch({ type: FETCH_STUDENTS_DATA_REQUEST });

try {
const response = camelCaseObject(await getStudentbyInstitutionAdmin(currentPage, filters));
dispatch({ type: 'FETCH_SUCCESS', payload: response.data });
dispatch({ type: FETCH_STUDENTS_DATA_SUCCESS, payload: response.data });
} catch (error) {
dispatch({ type: 'FETCH_FAILURE', payload: error });
dispatch({ type: FETCH_STUDENTS_DATA_FAILURE, payload: error });
logError(error);
}
};
Expand All @@ -109,11 +71,11 @@ const StudentsPage = () => {
}, [currentPage, filters]);

const handleOpenFiltersModal = () => {
dispatch({ type: 'OPEN_MODAL' });
dispatch({ type: OPEN_MODAL });
};

const handleCloseFiltersModal = () => {
dispatch({ type: 'CLOSE_MODAL' });
dispatch({ type: CLOSE_MODAL });
};

const handleApplyFilters = async () => {
Expand All @@ -129,7 +91,7 @@ const StudentsPage = () => {

const handlePagination = (targetPage) => {
setCurrentPage(targetPage);
dispatch({ type: 'UPDATE_CURRENT_PAGE', payload: targetPage });
dispatch({ type: UPDATE_CURRENT_PAGE, payload: targetPage });
fetchData();
};

Expand Down
58 changes: 58 additions & 0 deletions src/features/Students/StudentsPage/reducer.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
import {
FETCH_STUDENTS_DATA_REQUEST,
FETCH_STUDENTS_DATA_SUCCESS,
FETCH_STUDENTS_DATA_FAILURE,
UPDATE_CURRENT_PAGE,
OPEN_MODAL,
CLOSE_MODAL,
} from 'features/Students/actionTypes';
import { RequestStatus } from 'features/constants';

const reducer = (state, action) => {
switch (action.type) {
case FETCH_STUDENTS_DATA_REQUEST:
return { ...state, status: RequestStatus.LOADING };
case FETCH_STUDENTS_DATA_SUCCESS: {
const { results, count, numPages } = action.payload;
return {
...state,
status: RequestStatus.SUCCESS,
data: results,
numPages,
count,
};
}
case FETCH_STUDENTS_DATA_FAILURE:
return {
...state,
status: RequestStatus.ERROR,
error: action.payload,
};
case UPDATE_CURRENT_PAGE:
return {
...state,
currentPage: action.payload,
};
case OPEN_MODAL:
return {
...state,
filters: {
...state.filters,
isOpenFilters: true,
},
};
case CLOSE_MODAL:
return {
...state,
filters: {
...state.filters,
isOpenFilters: false,
errors: {},
},
};
default:
return state;
}
};

export default reducer;
Loading

0 comments on commit c709744

Please sign in to comment.