diff --git a/src/components/FilterBar.jsx b/src/components/FilterBar.jsx
index 0c506279..da5b9427 100644
--- a/src/components/FilterBar.jsx
+++ b/src/components/FilterBar.jsx
@@ -1,4 +1,4 @@
-import React, { useCallback, useMemo, useState } from 'react';
+import { useCallback, useMemo, useState } from 'react';
import PropTypes from 'prop-types';
import {
@@ -8,7 +8,7 @@ import { Tune } from '@openedx/paragon/icons';
import { capitalize, toString } from 'lodash';
import { useSelector } from 'react-redux';
-import { injectIntl, intlShape } from '@edx/frontend-platform/i18n';
+import { useIntl } from '@edx/frontend-platform/i18n';
import {
PostsStatusFilter, RequestStatus,
@@ -19,12 +19,12 @@ import messages from '../discussions/posts/post-filter-bar/messages';
import { ActionItem } from '../discussions/posts/post-filter-bar/PostFilterBar';
const FilterBar = ({
- intl,
filters,
selectedFilters,
onFilterChange,
showCohortsFilter,
}) => {
+ const intl = useIntl();
const [isOpen, setOpen] = useState(false);
const cohorts = useSelector(selectCourseCohorts);
const { status } = useSelector(state => state.cohorts);
@@ -192,7 +192,6 @@ const FilterBar = ({
};
FilterBar.propTypes = {
- intl: intlShape.isRequired,
filters: PropTypes.arrayOf(PropTypes.shape({
name: PropTypes.string,
filters: PropTypes.arrayOf(PropTypes.string),
@@ -211,4 +210,4 @@ FilterBar.defaultProps = {
showCohortsFilter: false,
};
-export default injectIntl(FilterBar);
+export default FilterBar;
diff --git a/src/components/Head/Head.jsx b/src/components/Head/Head.jsx
index 2cda3b1d..f81ab632 100644
--- a/src/components/Head/Head.jsx
+++ b/src/components/Head/Head.jsx
@@ -1,23 +1,21 @@
-import React from 'react';
-
import { Helmet } from 'react-helmet';
import { getConfig } from '@edx/frontend-platform';
-import { injectIntl, intlShape } from '@edx/frontend-platform/i18n';
+import { useIntl } from '@edx/frontend-platform/i18n';
import messages from './messages';
-const Head = ({ intl }) => (
-
-
- {intl.formatMessage(messages['discussions.page.title'], { siteName: getConfig().SITE_NAME })}
-
-
-
-);
+const Head = () => {
+ const intl = useIntl();
-Head.propTypes = {
- intl: intlShape.isRequired,
+ return (
+
+
+ {intl.formatMessage(messages['discussions.page.title'], { siteName: getConfig().SITE_NAME })}
+
+
+
+ );
};
-export default injectIntl(Head);
+export default Head;
diff --git a/src/discussions/common/ActionsDropdown.test.jsx b/src/discussions/common/ActionsDropdown.test.jsx
index 5a40f64a..6da4385f 100644
--- a/src/discussions/common/ActionsDropdown.test.jsx
+++ b/src/discussions/common/ActionsDropdown.test.jsx
@@ -242,6 +242,25 @@ describe('ActionsDropdown', () => {
await waitFor(() => expect(screen.queryByText('Copy link')).not.toBeInTheDocument());
});
+ it('should close the dropdown when pressing escape', async () => {
+ const discussionObject = buildTestContent({ editable_fields: ['copy_link'] }).discussion;
+ await mockThreadAndComment(discussionObject);
+ renderComponent({ ...camelCaseObject(discussionObject) });
+
+ const openButton = await findOpenActionsDropdownButton();
+ await act(async () => {
+ fireEvent.click(openButton);
+ });
+
+ await waitFor(() => expect(screen.getByRole('button', { name: 'Copy link' })).toBeInTheDocument());
+
+ await act(async () => {
+ fireEvent.keyDown(document.body, { key: 'Escape', code: 'Escape' });
+ });
+
+ await waitFor(() => expect(screen.queryByRole('button', { name: 'Copy link' })).toBeNull());
+ });
+
describe.each(canPerformActionTestData)('Actions', ({
testFor, action, label, ...commentOrPost
}) => {
diff --git a/src/discussions/in-context-topics/components/BackButton.jsx b/src/discussions/in-context-topics/components/BackButton.jsx
index fb182831..12d89ef7 100644
--- a/src/discussions/in-context-topics/components/BackButton.jsx
+++ b/src/discussions/in-context-topics/components/BackButton.jsx
@@ -1,17 +1,19 @@
-import React from 'react';
import PropTypes from 'prop-types';
import { Icon, IconButton, Spinner } from '@openedx/paragon';
import { ArrowBack } from '@openedx/paragon/icons';
import { useNavigate } from 'react-router-dom';
-import { injectIntl, intlShape } from '@edx/frontend-platform/i18n';
+import { useIntl } from '@edx/frontend-platform/i18n';
import messages from '../messages';
const BackButton = ({
- intl, path, title, loading,
+ path,
+ title,
+ loading,
}) => {
+ const intl = useIntl();
const navigate = useNavigate();
return (
@@ -35,7 +37,6 @@ const BackButton = ({
};
BackButton.propTypes = {
- intl: intlShape.isRequired,
path: PropTypes.shape({}).isRequired,
title: PropTypes.string.isRequired,
loading: PropTypes.bool,
@@ -45,4 +46,4 @@ BackButton.defaultProps = {
loading: false,
};
-export default injectIntl(BackButton);
+export default BackButton;
diff --git a/src/discussions/in-context-topics/topic-search/TopicSearchResultBar.jsx b/src/discussions/in-context-topics/topic-search/TopicSearchResultBar.jsx
index 77e9c451..16616c81 100644
--- a/src/discussions/in-context-topics/topic-search/TopicSearchResultBar.jsx
+++ b/src/discussions/in-context-topics/topic-search/TopicSearchResultBar.jsx
@@ -1,14 +1,13 @@
-import React from 'react';
-
import { SearchField } from '@openedx/paragon';
import { useDispatch } from 'react-redux';
-import { injectIntl, intlShape } from '@edx/frontend-platform/i18n';
+import { useIntl } from '@edx/frontend-platform/i18n';
import { setFilter } from '../data';
import messages from '../messages';
-const TopicSearchResultBar = ({ intl }) => {
+const TopicSearchResultBar = () => {
+ const intl = useIntl();
const dispatch = useDispatch();
return (
@@ -23,8 +22,4 @@ const TopicSearchResultBar = ({ intl }) => {
);
};
-TopicSearchResultBar.propTypes = {
- intl: intlShape.isRequired,
-};
-
-export default injectIntl(TopicSearchResultBar);
+export default TopicSearchResultBar;
diff --git a/src/discussions/in-context-topics/topic-search/TopicSearchResultBar.test.jsx b/src/discussions/in-context-topics/topic-search/TopicSearchResultBar.test.jsx
new file mode 100644
index 00000000..5edce28e
--- /dev/null
+++ b/src/discussions/in-context-topics/topic-search/TopicSearchResultBar.test.jsx
@@ -0,0 +1,65 @@
+import { fireEvent, render } from '@testing-library/react';
+import { useDispatch } from 'react-redux';
+
+import { IntlProvider } from '@edx/frontend-platform/i18n';
+
+import { setFilter } from '../data';
+import messages from '../messages';
+import TopicSearchResultBar from './TopicSearchResultBar';
+
+jest.mock('react-redux', () => ({
+ useDispatch: jest.fn(),
+}));
+
+jest.mock('../data', () => ({
+ setFilter: jest.fn(),
+}));
+
+describe('TopicSearchResultBar', () => {
+ const dispatch = jest.fn();
+
+ beforeEach(() => {
+ useDispatch.mockReturnValue(dispatch);
+ setFilter.mockReturnValue({ type: 'SET_FILTER' });
+ });
+
+ afterEach(() => {
+ jest.clearAllMocks();
+ });
+
+ it('should render search field', () => {
+ const { getByPlaceholderText } = render(
+
+
+ ,
+ );
+ expect(getByPlaceholderText(messages.searchTopics.defaultMessage)).toBeInTheDocument();
+ });
+
+ it('should dispatch setFilter on submit', () => {
+ const { getByPlaceholderText } = render(
+
+
+ ,
+ );
+ const searchField = getByPlaceholderText(messages.searchTopics.defaultMessage);
+ fireEvent.change(searchField, { target: { value: 'test query' } });
+ fireEvent.submit(searchField);
+
+ expect(setFilter).toHaveBeenCalledWith('test query');
+ expect(dispatch).toHaveBeenCalled();
+ });
+
+ it('should dispatch setFilter on change', () => {
+ const { getByPlaceholderText } = render(
+
+
+ ,
+ );
+ const searchField = getByPlaceholderText(messages.searchTopics.defaultMessage);
+ fireEvent.change(searchField, { target: { value: 'test' } });
+
+ expect(setFilter).toHaveBeenCalledWith('test');
+ expect(dispatch).toHaveBeenCalled();
+ });
+});