Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

PADV-706 Add filters to instructors table #12

Merged
merged 1 commit into from
Oct 11, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
81 changes: 81 additions & 0 deletions src/features/Instructors/InstructorsFilters/_test_/index.test.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
import React from 'react';
import { render, fireEvent, act } from '@testing-library/react';
import InstructorsFilters from 'features/Instructors/InstructorsFilters';
import '@testing-library/jest-dom/extend-expect';

describe('InstructorsFilters Component', () => {
test('call service when apply filters', async () => {
const fetchData = jest.fn();
const resetPagination = jest.fn();
const { getByPlaceholderText, getByText } = render(
<InstructorsFilters fetchData={fetchData} resetPagination={resetPagination} />,
);

const button = getByText('Filters');
await act(async () => {
fireEvent.click(button);
});

const nameInput = getByPlaceholderText('Enter Instructor Name');
const emailInput = getByPlaceholderText('Enter Instructor Email');
const classNameInput = getByPlaceholderText('Enter Class Name');
const buttonApplyFilters = getByText('Apply Filters');

expect(nameInput).toBeInTheDocument();
expect(emailInput).toBeInTheDocument();
expect(classNameInput).toBeInTheDocument();

fireEvent.change(nameInput, { target: { value: 'Name' } });
fireEvent.change(emailInput, { target: { value: '[email protected]' } });
fireEvent.change(classNameInput, { target: { value: 'CCX01' } });

expect(nameInput).toHaveValue('Name');
expect(emailInput).toHaveValue('[email protected]');
expect(classNameInput).toHaveValue('CCX01');

await act(async () => {
fireEvent.click(buttonApplyFilters);
});

expect(fetchData).toHaveBeenCalledTimes(1);
});

test('clear filters', async () => {
const fetchData = jest.fn();
const resetPagination = jest.fn();
const { getByPlaceholderText, getByText } = render(
<InstructorsFilters fetchData={fetchData} resetPagination={resetPagination} />,
);

const button = getByText('Filters');
await act(async () => {
fireEvent.click(button);
});

const nameInput = getByPlaceholderText('Enter Instructor Name');
const emailInput = getByPlaceholderText('Enter Instructor Email');
const classNameInput = getByPlaceholderText('Enter Class Name');
const buttonClearFilters = getByText('Clear');

expect(nameInput).toBeInTheDocument();
expect(emailInput).toBeInTheDocument();
expect(classNameInput).toBeInTheDocument();

fireEvent.change(nameInput, { target: { value: 'Name' } });
fireEvent.change(emailInput, { target: { value: '[email protected]' } });
fireEvent.change(classNameInput, { target: { value: 'CCX01' } });

expect(nameInput).toHaveValue('Name');
expect(emailInput).toHaveValue('[email protected]');
expect(classNameInput).toHaveValue('CCX01');

await act(async () => {
fireEvent.click(buttonClearFilters);
});

expect(nameInput).toHaveValue('');
expect(emailInput).toHaveValue('');
expect(classNameInput).toHaveValue('');
expect(resetPagination).toHaveBeenCalledTimes(1);
});
});
79 changes: 79 additions & 0 deletions src/features/Instructors/InstructorsFilters/index.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
import React, { useState } from 'react';
import {
DropdownButton, Form, Col, Button,
} from '@edx/paragon';
import PropTypes from 'prop-types';

const initialFilterFormValues = {
instructorName: '',
instructorEmail: '',
ccxId: '',
};

const InstructorsFilters = ({ fetchData, resetPagination }) => {
const [filters, setFilters] = useState(initialFilterFormValues);

const handleInputChange = (e) => {
setFilters({
...filters,
[e.target.name]: e.target.value.trim(),
});
};

const handleApplyFilters = async () => {
fetchData(filters);
};

const handleCleanFilters = () => {
setFilters(initialFilterFormValues);
fetchData();
resetPagination();
};

return (
<DropdownButton title="Filters" variant="outline-primary">
<Form className="row justify-content-center px-3 py-2">
<Form.Group as={Col} className="mb-0">
<Form.Control
type="text"
floatingLabel="Name"
name="instructorName"
placeholder="Enter Instructor Name"
value={filters.instructorName}
onChange={handleInputChange}
className="mb-3 mr-0"
/>
<Form.Control
type="text"
floatingLabel="Email"
name="instructorEmail"
placeholder="Enter Instructor Email"
value={filters.instructorEmail}
onChange={handleInputChange}
className="mb-3 mr-0"
/>
<Form.Control
type="text"
floatingLabel="Class Name"
name="ccxId"
placeholder="Enter Class Name"
value={filters.ccxId}
onChange={handleInputChange}
className="mb-4 mr-0"
/>
<div className="d-flex justify-content-between">
<Button onClick={handleApplyFilters}>Apply Filters</Button>
<Button onClick={handleCleanFilters} variant="outline-primary">Clear</Button>
</div>
</Form.Group>
</Form>
</DropdownButton>
);
};

InstructorsFilters.propTypes = {
fetchData: PropTypes.func.isRequired,
resetPagination: PropTypes.func.isRequired,
};

export default InstructorsFilters;
13 changes: 11 additions & 2 deletions src/features/Instructors/InstructorsPage/index.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import {
Pagination,
} from '@edx/paragon';
import InstructorsTable from 'features/Instructors/InstructorsTable';
import InstructorsFilters from 'features/Instructors/InstructorsFilters';

import { getInstructorData } from 'features/Instructors/data/api';
import {
Expand All @@ -30,11 +31,11 @@ const InstructorsPage = () => {
const [state, dispatch] = useReducer(reducer, initialState);
const [currentPage, setCurrentPage] = useState(1);

const fetchData = async () => {
const fetchData = async (filters) => {
dispatch({ type: FETCH_INSTRUCTOR_DATA_REQUEST });

try {
const response = camelCaseObject(await getInstructorData(currentPage));
const response = camelCaseObject(await getInstructorData(currentPage, filters));
dispatch({ type: FETCH_INSTRUCTOR_DATA_SUCCESS, payload: response.data });
} catch (error) {
dispatch({ type: FETCH_INSTRUCTOR_DATA_FAILURE, payload: error });
Expand All @@ -52,9 +53,17 @@ const InstructorsPage = () => {
fetchData();
};

const resetPagination = () => {
setCurrentPage(1);
};

return (
<Container size="xl" className="px-3">
<h2>Instructors</h2>
<div className="d-flex justify-content-end">
<InstructorsFilters fetchData={fetchData} resetPagination={resetPagination} />
</div>

<InstructorsTable
data={state.data}
count={state.count}
Expand Down
2 changes: 1 addition & 1 deletion src/features/Instructors/InstructorsTable/index.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ const InstructorsTable = ({

return (
<IntlProvider locale="en">
<Row lassName="justify-content-center my-4 border-gray-300 bg-light-100 my-3">
<Row className="justify-content-center my-4 border-gray-300 bg-light-100 my-3">
anfbermudezme marked this conversation as resolved.
Show resolved Hide resolved
<Col xs={11}>
<DataTable
isSortable
Expand Down
3 changes: 2 additions & 1 deletion src/features/Instructors/data/api.js
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
import { getAuthenticatedHttpClient } from '@edx/frontend-platform/auth';
import { getConfig } from '@edx/frontend-platform';

function getInstructorData(page) {
function getInstructorData(page, filters) {
const apiV2BaseUrl = getConfig().COURSE_OPERATIONS_API_V2_BASE_URL;
const params = {
page,
...filters,
};

return getAuthenticatedHttpClient().get(
Expand Down