refactor: Replace injectIntl with the useIntl() hook (#798)

* refactor: Replace of injectIntl with useIntl

* test: improve coverage
This commit is contained in:
Diana Villalvazo
2025-08-26 12:58:53 -06:00
committed by GitHub
parent 28b1b1973b
commit 5bef624714
6 changed files with 110 additions and 33 deletions

View File

@@ -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;

View File

@@ -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 }) => (
<Helmet>
<title>
{intl.formatMessage(messages['discussions.page.title'], { siteName: getConfig().SITE_NAME })}
</title>
<link rel="shortcut icon" href={getConfig().FAVICON_URL} type="image/x-icon" />
</Helmet>
);
const Head = () => {
const intl = useIntl();
Head.propTypes = {
intl: intlShape.isRequired,
return (
<Helmet>
<title>
{intl.formatMessage(messages['discussions.page.title'], { siteName: getConfig().SITE_NAME })}
</title>
<link rel="shortcut icon" href={getConfig().FAVICON_URL} type="image/x-icon" />
</Helmet>
);
};
export default injectIntl(Head);
export default Head;

View File

@@ -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
}) => {

View File

@@ -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;

View File

@@ -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;

View File

@@ -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(
<IntlProvider locale="en">
<TopicSearchResultBar />
</IntlProvider>,
);
expect(getByPlaceholderText(messages.searchTopics.defaultMessage)).toBeInTheDocument();
});
it('should dispatch setFilter on submit', () => {
const { getByPlaceholderText } = render(
<IntlProvider locale="en">
<TopicSearchResultBar />
</IntlProvider>,
);
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(
<IntlProvider locale="en">
<TopicSearchResultBar />
</IntlProvider>,
);
const searchField = getByPlaceholderText(messages.searchTopics.defaultMessage);
fireEvent.change(searchField, { target: { value: 'test' } });
expect(setFilter).toHaveBeenCalledWith('test');
expect(dispatch).toHaveBeenCalled();
});
});