diff --git a/src/course-home/courseware-search/CoursewareResultsFilter.jsx b/src/course-home/courseware-search/CoursewareResultsFilter.jsx index 233647cab0..7f1f8cac17 100644 --- a/src/course-home/courseware-search/CoursewareResultsFilter.jsx +++ b/src/course-home/courseware-search/CoursewareResultsFilter.jsx @@ -1,4 +1,4 @@ -import React from 'react'; +import React, { useMemo } from 'react'; import { injectIntl, intlShape } from '@edx/frontend-platform/i18n'; import { Tabs, Tab } from '@edx/paragon'; @@ -8,12 +8,15 @@ import messages from './messages'; import { useCoursewareSearchParams } from './hooks'; import { useModel } from '../../generic/model-store'; -const noFilterKey = 'none'; -const noFilterLabel = 'All content'; - -export const filteredResultsBySelection = ({ key = noFilterKey, results = [] }) => ( - key === noFilterKey ? results : results.filter(({ type }) => type === key) -); +const allFilterKey = 'all'; +const otherFilterKey = 'other'; +const allowedFilterKeys = { + [allFilterKey]: true, + text: true, + video: true, + sequence: true, + [otherFilterKey]: true, +}; export const CoursewareSearchResultsFilter = ({ intl }) => { const { courseId } = useParams(); @@ -22,24 +25,25 @@ export const CoursewareSearchResultsFilter = ({ intl }) => { if (!lastSearch || !lastSearch?.results?.length) { return null; } - const { total, results } = lastSearch; + const { results: data = [] } = lastSearch; + + const results = useMemo(() => data.reduce((acc, { type, ...rest }) => { + acc[allFilterKey] = [...(acc[allFilterKey] || []), { type: allFilterKey, ...rest }]; + if (type === allFilterKey) { return acc; } - const filters = [ - { - key: noFilterKey, - label: noFilterLabel, - count: total, - }, - ...lastSearch.filters, - ]; + let targetKey = otherFilterKey; + if (allowedFilterKeys[type]) { targetKey = type; } + acc[targetKey] = [...(acc[targetKey] || []), { type: targetKey, ...rest }]; + return acc; + }, {}), [data]); - const activeKey = filters.find(({ key }) => key === filterKeyword)?.key || noFilterKey; + const filters = useMemo(() => Object.keys(allowedFilterKeys).map((key) => ({ + key, + label: intl.formatMessage(messages[`filter:${key}`]), + count: results[key].length, + })), [results]); - const getFilterTitle = (key, fallback) => { - const msg = messages[`filter:${key}`]; - if (!msg) { return fallback; } - return intl.formatMessage(msg); - }; + const activeKey = allowedFilterKeys[filterKeyword] ? filterKeyword : allFilterKey; return ( { onSelect={setFilter} > {filters.map(({ key, label }) => ( - - + + ))} diff --git a/src/course-home/courseware-search/CoursewareResultsFilter.test.jsx b/src/course-home/courseware-search/CoursewareResultsFilter.test.jsx index 370ca20104..26c56f01ae 100644 --- a/src/course-home/courseware-search/CoursewareResultsFilter.test.jsx +++ b/src/course-home/courseware-search/CoursewareResultsFilter.test.jsx @@ -8,7 +8,7 @@ import { screen, waitFor, } from '../../setupTest'; -import { CoursewareSearchResultsFilter, filteredResultsBySelection } from './CoursewareResultsFilter'; +import { CoursewareSearchResultsFilter } from './CoursewareResultsFilter'; import { useCoursewareSearchParams } from './hooks'; import initializeStore from '../../store'; import { useModel } from '../../generic/model-store'; @@ -19,27 +19,6 @@ jest.mock('../../generic/model-store', () => ({ useModel: jest.fn(), })); -const mockResults = [ - { - id: 'video-1', type: 'video', title: 'video_title', score: 3, contentHits: 1, url: '/video-1', location: ['path1', 'path2'], - }, - { - id: 'video-2', type: 'video', title: 'video_title2', score: 2, contentHits: 1, url: '/video-2', location: ['path1', 'path2'], - }, - { - id: 'document-1', type: 'document', title: 'document_title', score: 3, contentHits: 1, url: '/document-1', location: ['path1', 'path2'], - }, - { - id: 'text-1', type: 'text', title: 'text_title1', score: 3, contentHits: 1, url: '/text-1', location: ['path1', 'path2'], - }, - { - id: 'text-2', type: 'text', title: 'text_title2', score: 2, contentHits: 1, url: '/text-2', location: ['path1', 'path2'], - }, - { - id: 'text-3', type: 'text', title: 'text_title3', score: 1, contentHits: 1, url: '/text-3', location: ['path1', 'path2'], - }, -]; - const decodedCourseId = 'course-v1:edX+DemoX+Demo_Course'; const decodedSequenceId = 'block-v1:edX+DemoX+Demo_Course+type@sequential+block@edx_introduction'; const decodedUnitId = 'block-v1:edX+DemoX+Demo_Course+type@vertical+block@vertical_0270f6de40fc'; @@ -73,38 +52,6 @@ function renderComponent(props = {}) { describe('CoursewareSearchResultsFilter', () => { beforeAll(initializeMockApp); - describe('filteredResultsBySelection', () => { - it('returns a no values array when no results are provided', () => { - const results = filteredResultsBySelection({}); - - expect(results.length).toEqual(0); - }); - - it('returns all values when no key value is provided', () => { - const results = filteredResultsBySelection({ results: mockResults }); - - expect(results.length).toEqual(mockResults.length); - }); - - it('returns only "video"-typed elements when the key value "video" is given', () => { - const results = filteredResultsBySelection({ key: 'video', results: mockResults }); - - expect(results.length).toEqual(2); - }); - - it('returns only "course_outline"-typed elements when the key value "document" is given', () => { - const results = filteredResultsBySelection({ key: 'document', results: mockResults }); - - expect(results.length).toEqual(1); - }); - - it('returns only "text"-typed elements when the key value "text" is given', () => { - const results = filteredResultsBySelection({ key: 'text', results: mockResults }); - - expect(results.length).toEqual(3); - }); - }); - describe('', () => { beforeEach(() => { jest.clearAllMocks(); @@ -117,8 +64,11 @@ describe('CoursewareSearchResultsFilter', () => { await renderComponent(); await waitFor(() => { - expect(screen.queryByTestId('courseware-search-results-tabs')).toBeInTheDocument(); - expect(screen.queryByText(/All content/)).toBeInTheDocument(); + expect(screen.queryByTestId('courseware-search-results-tabs-all')).toBeInTheDocument(); + expect(screen.queryByTestId('courseware-search-results-tabs-text')).toBeInTheDocument(); + expect(screen.queryByTestId('courseware-search-results-tabs-video')).toBeInTheDocument(); + expect(screen.queryByTestId('courseware-search-results-tabs-sequence')).toBeInTheDocument(); + expect(screen.queryByTestId('courseware-search-results-tabs-other')).toBeInTheDocument(); }); }); }); diff --git a/src/course-home/courseware-search/CoursewareSearchResults.jsx b/src/course-home/courseware-search/CoursewareSearchResults.jsx index f579e7bfd8..5c7bbfde25 100644 --- a/src/course-home/courseware-search/CoursewareSearchResults.jsx +++ b/src/course-home/courseware-search/CoursewareSearchResults.jsx @@ -70,14 +70,13 @@ const CoursewareSearchResults = ({ results }) => { }; CoursewareSearchResults.propTypes = { - results: PropTypes.arrayOf(PropTypes.objectOf({ + results: PropTypes.arrayOf(PropTypes.shape({ id: PropTypes.string, title: PropTypes.string, type: PropTypes.string, location: PropTypes.arrayOf(PropTypes.string), url: PropTypes.string, contentHits: PropTypes.number, - score: PropTypes.number, })), }; diff --git a/src/course-home/courseware-search/messages.js b/src/course-home/courseware-search/messages.js index 946e7ad945..96449f2299 100644 --- a/src/course-home/courseware-search/messages.js +++ b/src/course-home/courseware-search/messages.js @@ -53,8 +53,8 @@ const messages = defineMessages({ }, // These are translations for labeling the filters - 'filter:none': { - id: 'learn.coursewareSerch.filter:none', + 'filter:all': { + id: 'learn.coursewareSerch.filter:all', defaultMessage: 'All content', description: 'Label for the search results filter that shows all content (no filter).', }, @@ -68,6 +68,16 @@ const messages = defineMessages({ defaultMessage: 'Video', description: 'Label for the search results filter that shows results with video content.', }, + 'filter:sequence': { + id: 'learn.coursewareSerch.filter:sequence', + defaultMessage: 'Section', + description: 'Label for the search results filter that shows results with section content.', + }, + 'filter:other': { + id: 'learn.coursewareSerch.filter:other', + defaultMessage: 'Other', + description: 'Label for the search results filter that shows results with other content.', + }, }); export default messages;