Skip to content

Commit

Permalink
feat: change filter component in courses page
Browse files Browse the repository at this point in the history
  • Loading branch information
AuraAlba committed Nov 21, 2024
1 parent ba9ce7b commit cba5fef
Show file tree
Hide file tree
Showing 5 changed files with 86 additions and 69 deletions.
8 changes: 4 additions & 4 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@
"react": "16.14.0",
"react-dom": "16.14.0",
"react-intl": "^5.25.1",
"react-paragon-topaz": "1.19.1",
"react-paragon-topaz": "1.20.0",
"react-redux": "^7.2.9",
"react-router": "5.2.1",
"react-router-dom": "5.3.0",
Expand Down
34 changes: 14 additions & 20 deletions src/features/Courses/CoursesFilters/_test_/index.test.jsx
Original file line number Diff line number Diff line change
@@ -1,17 +1,17 @@
/* eslint-disable react/prop-types */
import React from 'react';
import { fireEvent, act } from '@testing-library/react';
import { fireEvent } from '@testing-library/react';
import CoursesFilters from 'features/Courses/CoursesFilters';
import '@testing-library/jest-dom/extend-expect';
import { initializeMockApp } from '@edx/frontend-platform/testing';
import { renderWithProviders } from 'test-utils';
import { allResultsOption } from 'features/constants';

jest.mock('react-select', () => function reactSelect({ options, valueR, onChange }) {
function handleChange(event) {
const option = options.find(
(optionR) => optionR.value === event.currentTarget.value,
);
onChange(option);
onChange({ id: event.currentTarget.value });

return event;
}

return (
Expand Down Expand Up @@ -69,43 +69,37 @@ describe('CoursesFilters Component', () => {
});
});

test('call service when apply filters', async () => {
const resetPagination = jest.fn();
test('should select a course', async () => {
const { getByText, getByTestId } = renderWithProviders(
<CoursesFilters resetPagination={resetPagination} />,
<CoursesFilters />,
{ preloadedState: mockStore },
);

const courseSelect = getByTestId('select');
const buttonApplyFilters = getByText('Apply');

expect(getByText('Find a primary course')).toBeInTheDocument();

expect(courseSelect).toBeInTheDocument();
fireEvent.change(courseSelect, {
target: { value: 'Demo Course 1' },
});

expect(getByText('Demo Course 1')).toBeInTheDocument();
await act(async () => {
fireEvent.click(buttonApplyFilters);
});
});

test('clear filters', async () => {
const resetPagination = jest.fn();
test('Should have option for all results ', async () => {
const { getByText, getByTestId } = renderWithProviders(
<CoursesFilters resetPagination={resetPagination} />,
<CoursesFilters />,
);

const courseSelect = getByTestId('select');
const buttonClearFilters = getByText('Reset');

expect(courseSelect).toBeInTheDocument();
expect(courseSelect).toBeInTheDocument();
fireEvent.change(courseSelect, {
target: { value: 'Demo Course 1' },
});
await act(async () => {
fireEvent.click(buttonClearFilters);
target: { value: allResultsOption.value },
});

expect(getByText('Show all search results')).toBeInTheDocument();
});
});
84 changes: 40 additions & 44 deletions src/features/Courses/CoursesFilters/index.jsx
Original file line number Diff line number Diff line change
@@ -1,44 +1,33 @@
import React, { useState, useEffect } from 'react';
import { useDispatch, useSelector } from 'react-redux';
import PropTypes from 'prop-types';

import { Col, Form } from '@edx/paragon';
import { Select, Button } from 'react-paragon-topaz';
import { logError } from '@edx/frontend-platform/logging';
import { Select } from 'react-paragon-topaz';

import { updateFilters, updateCurrentPage } from 'features/Courses/data/slice';
import { fetchCoursesData, fetchCoursesOptionsData } from 'features/Courses/data/thunks';

import { initialPage } from 'features/constants';
import { initialPage, styleFirstOption, allResultsOption } from 'features/constants';

const CoursesFilters = ({ resetPagination }) => {
const CoursesFilters = () => {
const dispatch = useDispatch();
const selectedInstitution = useSelector((state) => state.main.selectedInstitution);
const courses = useSelector((state) => state.courses.selectOptions);
const [courseOptions, setCourseOptions] = useState([]);
const [courseSelected, setCourseSelected] = useState(null);
const [inputCourse, setInputCourse] = useState('');

const isButtonDisabled = courseSelected === null;

const handleCoursesFilter = async (e) => {
e.preventDefault();
const form = e.target;
const formData = new FormData(form);
const formJson = Object.fromEntries(formData.entries());
dispatch(updateFilters(formJson));
try {
dispatch(updateCurrentPage(initialPage));
dispatch(fetchCoursesData(selectedInstitution.id, initialPage, formJson));
} catch (error) {
logError(error);
const filterOptions = (option, input) => {
if (input) {
return option.label.toLowerCase().includes(input) || option.value === allResultsOption.value;
}
return true;
};

const handleCleanFilters = () => {
dispatch(fetchCoursesData(selectedInstitution.id));
resetPagination();
setCourseSelected(null);
dispatch(updateFilters({}));
const handleInputChange = (value, { action }) => {
if (action === 'input-change') {
setInputCourse(value);
}
};

useEffect(() => {
Expand All @@ -57,46 +46,53 @@ const CoursesFilters = ({ resetPagination }) => {
}))
: [];

setCourseOptions(options);
setCourseOptions([allResultsOption, ...options]);
}, [courses]);

useEffect(() => {
if (Object.keys(selectedInstitution).length > 0) {
const params = {};

if (courseSelected) {
params.course_name = courseSelected.value === allResultsOption.value
? inputCourse : courseSelected.value;
}
dispatch(fetchCoursesData(selectedInstitution.id, initialPage, params));
setInputCourse('');
dispatch(updateFilters(params));
dispatch(updateCurrentPage(initialPage));
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [courseSelected, selectedInstitution]);

return (
<div className="filter-container justify-content-center row">
<div className="col-11 px-0">
<h3>Find a course</h3>
<Form className="row justify-content-center" onSubmit={handleCoursesFilter}>
<h3 className="mb-3">Find a primary course</h3>
<Form className="row justify-content-center">
<Form.Row className="col-12">
<Form.Group as={Col}>
<Form.Group as={Col} className="px-0">
<Select
placeholder="Course"
placeholder="Search by keyword, exam code or vendor"
name="course_name"
className="mr-2"
options={courseOptions}
onChange={option => setCourseSelected(option)}
value={courseSelected}
components={{ DropdownIndicator: () => null, IndicatorSeparator: () => null }}
isClearable
inputValue={inputCourse}
onInputChange={handleInputChange}
filterOption={filterOptions}
showSearchIcon
styles={styleFirstOption}
/>
</Form.Group>
<div className="d-flex col-3 justify-content-end align-items-start">
<Button
onClick={handleCleanFilters}
variant="tertiary"
text
className="mr-2"
disabled={isButtonDisabled}
>
Reset
</Button>
<Button type="submit" disabled={isButtonDisabled}>Apply</Button>
</div>
</Form.Row>
</Form>
</div>
</div>
);
};

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

export default CoursesFilters;
27 changes: 27 additions & 0 deletions src/features/constants.js
Original file line number Diff line number Diff line change
Expand Up @@ -98,3 +98,30 @@ export const modalDeleteText = {
title: 'Delete this class',
body: 'This action will permanently delete this class and cannot be undone. Booked seat in this class will not be affected by this action.',
};

/**
* Custom styles for first option underlline in selector.
*@object
*/
export const styleFirstOption = {
menuList: (base) => ({
...base,
boxShadow: '0 5px 5px -3px #0003, 0 8px 10px 1px #00000024, 0 3px 14px 2px #0000001f',
border: 0,
padding: 0,
borderRadius: '0px 0px 4px 4px;',
'& :first-child': {
textDecoration: 'underline',
color: '#007394',
},
}),
};

/**
* All result option.
* @constant {Object}
*/
export const allResultsOption = {
label: 'Show all search results',
value: 'all_results',
};

0 comments on commit cba5fef

Please sign in to comment.