feat: added waffle flag state for Courseware Search (#1199)
This commit is contained in:
30
src/course-home/courseware-search/CoursewareSearch.jsx
Normal file
30
src/course-home/courseware-search/CoursewareSearch.jsx
Normal file
@@ -0,0 +1,30 @@
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import { useParams } from 'react-router-dom';
|
||||
import { injectIntl, intlShape } from '@edx/frontend-platform/i18n';
|
||||
import { Button, Icon } from '@edx/paragon';
|
||||
import { Search } from '@edx/paragon/icons';
|
||||
import messages from './messages';
|
||||
import { fetchCoursewareSearchSettings } from '../data/thunks';
|
||||
|
||||
const CoursewareSearch = ({ intl, ...rest }) => {
|
||||
const { courseId } = useParams();
|
||||
const [enabled, setEnabled] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
fetchCoursewareSearchSettings(courseId).then(response => setEnabled(response.enabled));
|
||||
}, [courseId]);
|
||||
|
||||
if (!enabled) { return null; }
|
||||
|
||||
return (
|
||||
<Button variant="tertiary" size="sm" className="p-1 mt-2 mr-2 rounded-lg" aria-label={intl.formatMessage(messages.searchOpenAction)} data-testid="courseware-search-button" {...rest}>
|
||||
<Icon src={Search} />
|
||||
</Button>
|
||||
);
|
||||
};
|
||||
|
||||
CoursewareSearch.propTypes = {
|
||||
intl: intlShape.isRequired,
|
||||
};
|
||||
|
||||
export default injectIntl(CoursewareSearch);
|
||||
46
src/course-home/courseware-search/CoursewareSearch.test.jsx
Normal file
46
src/course-home/courseware-search/CoursewareSearch.test.jsx
Normal file
@@ -0,0 +1,46 @@
|
||||
import React from 'react';
|
||||
import {
|
||||
act,
|
||||
initializeMockApp,
|
||||
render,
|
||||
screen,
|
||||
waitFor,
|
||||
} from '../../setupTest';
|
||||
import { fetchCoursewareSearchSettings } from '../data/thunks';
|
||||
import { CoursewareSearch } from './index';
|
||||
|
||||
jest.mock('../data/thunks');
|
||||
|
||||
function renderComponent() {
|
||||
const { container } = render(<CoursewareSearch />);
|
||||
return container;
|
||||
}
|
||||
|
||||
describe('CoursewareSearch', () => {
|
||||
beforeAll(async () => {
|
||||
initializeMockApp();
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
jest.clearAllMocks();
|
||||
});
|
||||
|
||||
it('Should not render when the waffle flag is disabled', async () => {
|
||||
fetchCoursewareSearchSettings.mockImplementation(() => Promise.resolve({ enabled: false }));
|
||||
|
||||
await act(async () => renderComponent());
|
||||
await waitFor(() => {
|
||||
expect(fetchCoursewareSearchSettings).toHaveBeenCalledTimes(1);
|
||||
expect(screen.queryByTestId('courseware-search-button')).not.toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
||||
it('Should render when the waffle flag is enabled', async () => {
|
||||
fetchCoursewareSearchSettings.mockImplementation(() => Promise.resolve({ enabled: true }));
|
||||
await act(async () => renderComponent());
|
||||
await waitFor(() => {
|
||||
expect(fetchCoursewareSearchSettings).toHaveBeenCalledTimes(1);
|
||||
expect(screen.queryByTestId('courseware-search-button')).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
});
|
||||
2
src/course-home/courseware-search/index.js
Normal file
2
src/course-home/courseware-search/index.js
Normal file
@@ -0,0 +1,2 @@
|
||||
/* eslint-disable import/prefer-default-export */
|
||||
export { default as CoursewareSearch } from './CoursewareSearch';
|
||||
11
src/course-home/courseware-search/messages.js
Normal file
11
src/course-home/courseware-search/messages.js
Normal file
@@ -0,0 +1,11 @@
|
||||
import { defineMessages } from '@edx/frontend-platform/i18n';
|
||||
|
||||
const messages = defineMessages({
|
||||
searchOpenAction: {
|
||||
id: 'learn.coursewareSerch.openAction',
|
||||
defaultMessage: 'Search within this course',
|
||||
description: 'Aria-label for a button that will pop up Courseware Search.',
|
||||
},
|
||||
});
|
||||
|
||||
export default messages;
|
||||
@@ -445,3 +445,9 @@ export async function unsubscribeFromCourseGoal(token) {
|
||||
return getAuthenticatedHttpClient().post(url.href)
|
||||
.then(res => camelCaseObject(res));
|
||||
}
|
||||
|
||||
export async function getCoursewareSearchEnabledFlag(courseId) {
|
||||
const url = new URL(`${getConfig().LMS_BASE_URL}/courses/${courseId}/courseware-search/enabled/`);
|
||||
const { data } = await getAuthenticatedHttpClient().get(url.href);
|
||||
return { enabled: data.enabled || false };
|
||||
}
|
||||
|
||||
@@ -250,4 +250,36 @@ describe('Data layer integration tests', () => {
|
||||
expect(axiosMock.history.post[0].data).toEqual(`{"course_id":"${courseId}"}`);
|
||||
});
|
||||
});
|
||||
|
||||
describe('Test fetchCoursewareSearchSettings', () => {
|
||||
it('Should return enabled as true when enabled', async () => {
|
||||
const apiUrl = `${getConfig().LMS_BASE_URL}/courses/${courseId}/courseware-search/enabled/`;
|
||||
axiosMock.onGet(apiUrl).reply(200, { enabled: true });
|
||||
|
||||
const { enabled } = await thunks.fetchCoursewareSearchSettings(courseId);
|
||||
|
||||
expect(axiosMock.history.get[0].url).toEqual(apiUrl);
|
||||
expect(enabled).toBe(true);
|
||||
});
|
||||
|
||||
it('Should return enabled as false when disabled', async () => {
|
||||
const apiUrl = `${getConfig().LMS_BASE_URL}/courses/${courseId}/courseware-search/enabled/`;
|
||||
axiosMock.onGet(apiUrl).reply(200, { enabled: false });
|
||||
|
||||
const { enabled } = await thunks.fetchCoursewareSearchSettings(courseId);
|
||||
|
||||
expect(axiosMock.history.get[0].url).toEqual(apiUrl);
|
||||
expect(enabled).toBe(false);
|
||||
});
|
||||
|
||||
it('Should return enabled as false on error', async () => {
|
||||
const apiUrl = `${getConfig().LMS_BASE_URL}/courses/${courseId}/courseware-search/enabled/`;
|
||||
axiosMock.onGet(apiUrl).networkError();
|
||||
|
||||
const { enabled } = await thunks.fetchCoursewareSearchSettings(courseId);
|
||||
|
||||
expect(axiosMock.history.get[0].url).toEqual(apiUrl);
|
||||
expect(enabled).toBe(false);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -12,6 +12,7 @@ import {
|
||||
postDismissWelcomeMessage,
|
||||
postRequestCert,
|
||||
getLiveTabIframe,
|
||||
getCoursewareSearchEnabledFlag,
|
||||
} from './api';
|
||||
|
||||
import {
|
||||
@@ -139,3 +140,12 @@ export function processEvent(eventData, getTabData) {
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
export async function fetchCoursewareSearchSettings(courseId) {
|
||||
try {
|
||||
const { enabled } = await getCoursewareSearchEnabledFlag(courseId);
|
||||
return { enabled };
|
||||
} catch (e) {
|
||||
return { enabled: false };
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,11 +5,15 @@ import classNames from 'classnames';
|
||||
|
||||
import messages from './messages';
|
||||
import Tabs from '../generic/tabs/Tabs';
|
||||
import { CoursewareSearch } from '../course-home/courseware-search';
|
||||
|
||||
const CourseTabsNavigation = ({
|
||||
activeTabSlug, className, tabs, intl,
|
||||
}) => (
|
||||
<div id="courseTabsNavigation" className={classNames('course-tabs-navigation', className)}>
|
||||
<div className="float-right">
|
||||
<CoursewareSearch />
|
||||
</div>
|
||||
<div className="container-xl">
|
||||
<Tabs
|
||||
className="nav-underline-tabs"
|
||||
|
||||
Reference in New Issue
Block a user