Merge branch 'master' of https://github.com/openedx/frontend-app-learner-dashboard into mashal-m/replace-edx/paragon-frontend-build

This commit is contained in:
Bilal Qamar
2024-02-29 11:05:44 +05:00
52 changed files with 85 additions and 1276 deletions

1
.env
View File

@@ -41,5 +41,4 @@ ACCOUNT_PROFILE_URL=''
ENABLE_NOTICES=''
CAREER_LINK_URL=''
OPTIMIZELY_FULL_STACK_SDK_KEY=''
EXPERIMENT_08_23_VAN_PAINTED_DOOR=true
ENABLE_EDX_PERSONAL_DASHBOARD=false

View File

@@ -47,5 +47,4 @@ ACCOUNT_PROFILE_URL='http://account-profile-url.test'
ENABLE_NOTICES=''
CAREER_LINK_URL=''
OPTIMIZELY_FULL_STACK_SDK_KEY='SDK Key'
EXPERIMENT_08_23_VAN_PAINTED_DOOR=true
ENABLE_EDX_PERSONAL_DASHBOARD=true

View File

@@ -1,6 +1,6 @@
import { StrictDict } from 'utils';
import { defineMessages } from '@edx/frontend-platform/i18n';
export const messages = StrictDict({
const messages = defineMessages({
supportTitle: {
id: 'zendesk.supportTitle',
description: 'Title for the support button',

View File

@@ -1,6 +1,6 @@
import { StrictDict } from 'utils';
import { defineMessages } from '@edx/frontend-platform/i18n';
export const messages = StrictDict({
const messages = defineMessages({
upgrade: {
id: 'learner-dash.courseCard.actions.upgrade',
description: 'Course card upgrade button text',

View File

@@ -1,6 +1,6 @@
import { StrictDict } from 'utils';
import { defineMessages } from '@edx/frontend-platform/i18n';
export const messages = StrictDict({
const messages = defineMessages({
error: {
id: 'learner-dash.courseCard.banners.credit.error',
description: '',

View File

@@ -1,6 +1,6 @@
import { StrictDict } from 'utils';
import { defineMessages } from '@edx/frontend-platform/i18n';
export const messages = StrictDict({
const messages = defineMessages({
approved: {
id: 'learner-dash.courseCard.banners.credit.approved',
description: '',

View File

@@ -1,6 +1,6 @@
import { StrictDict } from 'utils';
import { defineMessages } from '@edx/frontend-platform/i18n';
export const messages = StrictDict({
const messages = defineMessages({
relatedPrograms: {
id: 'learner-dash.courseCard.banners.relatedPrograms',
description: 'title for related programs banner',

View File

@@ -1,6 +1,6 @@
import { StrictDict } from 'utils';
import { defineMessages } from '@edx/frontend-platform/i18n';
export const messages = StrictDict({
const messages = defineMessages({
auditAccessExpired: {
id: 'learner-dash.courseCard.banners.auditAccessExpired',
description: 'Audit access expiration banner message',

View File

@@ -1,6 +1,6 @@
import { StrictDict } from 'utils';
import { defineMessages } from '@edx/frontend-platform/i18n';
export const messages = StrictDict({
const messages = defineMessages({
accessExpired: {
id: 'learner-dash.courseCard.CourseCardDetails.accessExpired',
description: 'Course access expiration date message on course card for expired access.',

View File

@@ -21,6 +21,7 @@ jest.mock('tracking', () => ({
}));
jest.mock('@edx/frontend-platform/i18n', () => ({
...jest.requireActual('@edx/frontend-platform/i18n'),
useIntl: jest.fn().mockReturnValue({
formatMessage: jest.requireActual('@edx/react-unit-test-utils').formatMessage,
}),

View File

@@ -12,6 +12,7 @@ import * as hooks from './hooks';
import CourseCardMenu, { testIds } from '.';
jest.mock('@edx/frontend-platform/i18n', () => ({
...jest.requireActual('@edx/frontend-platform/i18n'),
useIntl: jest.fn().mockReturnValue({
formatMessage: jest.requireActual('@edx/react-unit-test-utils').formatMessage,
}),

View File

@@ -1,6 +1,6 @@
import { StrictDict } from 'utils';
import { defineMessages } from '@edx/frontend-platform/i18n';
export const messages = StrictDict({
const messages = defineMessages({
unenroll: {
id: 'learner-dash.courseCardMenu.unenroll',
description: 'Course unenroll menu button',

View File

@@ -1,6 +1,6 @@
import { StrictDict } from 'utils';
import { defineMessages } from '@edx/frontend-platform/i18n';
export const messages = StrictDict({
const messages = defineMessages({
bannerAlt: {
id: 'learner-dash.courseCard.bannerAlt',
description: 'Course card banner alt-text',

View File

@@ -1,11 +1,13 @@
import React from 'react';
import PropTypes from 'prop-types';
import { useIntl } from '@edx/frontend-platform/i18n';
import { FilterKeys } from 'data/constants/app';
import { Form } from '@openedx/paragon';
import Checkbox from './Checkbox';
import messages from '../messages';
export const filterOrder = [
FilterKeys.inProgress,
@@ -18,20 +20,23 @@ export const filterOrder = [
export const FilterForm = ({
filters,
handleFilterChange,
}) => (
<Form.Group>
<div className="filter-form-heading mb-1">Course Status</div>
<Form.CheckboxSet
name="course-status-filters"
onChange={handleFilterChange}
value={filters}
>
{filterOrder.map(filterKey => (
<Checkbox filterKey={filterKey} key={filterKey} />
))}
</Form.CheckboxSet>
</Form.Group>
);
}) => {
const { formatMessage } = useIntl();
return (
<Form.Group>
<div className="filter-form-heading mb-1">{formatMessage(messages.courseStatus)}</div>
<Form.CheckboxSet
name="course-status-filters"
onChange={handleFilterChange}
value={filters}
>
{filterOrder.map(filterKey => (
<Checkbox filterKey={filterKey} key={filterKey} />
))}
</Form.CheckboxSet>
</Form.Group>
);
};
FilterForm.propTypes = {
filters: PropTypes.arrayOf(PropTypes.string).isRequired,
handleFilterChange: PropTypes.func.isRequired,

View File

@@ -1,6 +1,11 @@
import { defineMessages } from '@edx/frontend-platform/i18n';
const messages = defineMessages({
courseStatus: {
id: 'learner-dash.courseListFilters.courseStatus',
description: 'course status filter form heading',
defaultMessage: 'Course Status',
},
inProgress: {
id: 'learner-dash.courseListFilters.inProgress',
description: 'in-progress filter checkbox label for course list filters',

View File

@@ -1,7 +1,7 @@
/* eslint-disable quotes */
import { StrictDict } from 'utils';
import { defineMessages } from '@edx/frontend-platform/i18n';
export const messages = StrictDict({
const messages = defineMessages({
header: {
id: 'learner-dash.emailSettings.header',
description: 'Header for email settings modal',

View File

@@ -6,10 +6,8 @@ import { useIntl } from '@edx/frontend-platform/i18n';
import { AppContext } from '@edx/frontend-platform/react';
import { Button, Badge } from '@openedx/paragon';
import WidgetNavbar from 'containers/WidgetContainers/WidgetNavbar';
import urls from 'data/services/lms/urls';
import { reduxHooks } from 'hooks';
import { COLLAPSED_NAVBAR } from 'widgets/RecommendationsPaintedDoorBtn/constants';
import { findCoursesNavDropdownClicked } from '../hooks';
import messages from '../messages';
@@ -45,7 +43,6 @@ export const CollapseMenuBody = ({ isOpen }) => {
>
{formatMessage(messages.discoverNew)}
</Button>
<WidgetNavbar placement={COLLAPSED_NAVBAR} />
<Button as="a" href={getConfig().SUPPORT_URL} variant="inverse-primary">
{formatMessage(messages.help)}
</Button>

View File

@@ -26,9 +26,6 @@ exports[`CollapseMenuBody render 1`] = `
>
Discover New
</Button>
<WidgetNavbar
placement="collapsedNavbar"
/>
<Button
as="a"
href="http://localhost:18000/support"
@@ -97,9 +94,6 @@ exports[`CollapseMenuBody render unauthenticated 1`] = `
>
Discover New
</Button>
<WidgetNavbar
placement="collapsedNavbar"
/>
<Button
as="a"
href="http://localhost:18000/support"

View File

@@ -33,9 +33,6 @@ exports[`ExpandedHeader render 1`] = `
>
Discover New
</Button>
<WidgetNavbar
placement="expendedNavbar"
/>
<span
className="flex-grow-1"
/>

View File

@@ -4,10 +4,8 @@ import { getConfig } from '@edx/frontend-platform';
import { useIntl } from '@edx/frontend-platform/i18n';
import { Button } from '@openedx/paragon';
import WidgetNavbar from 'containers/WidgetContainers/WidgetNavbar';
import urls from 'data/services/lms/urls';
import { reduxHooks } from 'hooks';
import { EXPANDED_NAVBAR } from 'widgets/RecommendationsPaintedDoorBtn/constants';
import AuthenticatedUserDropdown from './AuthenticatedUserDropdown';
import { useIsCollapsed, findCoursesNavClicked } from '../hooks';
@@ -57,7 +55,6 @@ export const ExpandedHeader = () => {
>
{formatMessage(messages.discoverNew)}
</Button>
<WidgetNavbar placement={EXPANDED_NAVBAR} />
<span className="flex-grow-1" />
<Button
as="a"

View File

@@ -1,7 +1,7 @@
/* eslint-disable quotes */
import { StrictDict } from 'utils';
import { defineMessages } from '@edx/frontend-platform/i18n';
export const messages = StrictDict({
const messages = defineMessages({
header: {
id: 'learner-dash.relatedPrograms.header',
description: 'Header for related settings modal',

View File

@@ -1,7 +1,7 @@
/* eslint-disable quotes */
import { StrictDict } from 'utils';
import { defineMessages } from '@edx/frontend-platform/i18n';
export const messages = StrictDict({
const messages = defineMessages({
changeOrLeaveHeader: {
id: 'learner-dash.selectSession.changeOrLeaveHeader',
description: 'Header for session that allow leave option',

View File

@@ -1,7 +1,7 @@
/* eslint-disable quotes */
import { StrictDict } from 'utils';
import { defineMessages } from '@edx/frontend-platform/i18n';
export const messages = StrictDict({
const messages = defineMessages({
confirmHeader: {
id: 'learner-dash.unenrollConfirm.confirm.header',
description: 'Header for confirm unenroll modal',

View File

@@ -1,29 +0,0 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`AppWrapper WidgetContainer component output no experiments are active snapshot 1`] = `
<div>
This is some
<b>
test
</b>
<i>
content
</i>
</div>
`;
exports[`AppWrapper WidgetContainer component output painted door experiment is active (08/23) snapshot 1`] = `
<PaintedDoorExperimentProvider>
<div>
This is some
<b>
test
</b>
<i>
content
</i>
</div>
</PaintedDoorExperimentProvider>
`;

View File

@@ -1,20 +1,8 @@
import React from 'react';
import PropTypes from 'prop-types';
import PaintedDoorExperimentProvider from 'widgets/RecommendationsPaintedDoorBtn/PaintedDoorExperimentContext';
export const AppWrapper = ({
children,
}) => {
if (process.env.EXPERIMENT_08_23_VAN_PAINTED_DOOR) {
return (
<PaintedDoorExperimentProvider>
{children}
</PaintedDoorExperimentProvider>
);
}
return children;
};
}) => children;
AppWrapper.propTypes = {
children: PropTypes.oneOfType([
PropTypes.node,

View File

@@ -1,56 +0,0 @@
import React from 'react';
import { shallow } from '@edx/react-unit-test-utils';
import PaintedDoorExperimentProvider from 'widgets/RecommendationsPaintedDoorBtn/PaintedDoorExperimentContext';
import AppWrapper from '.';
jest.mock(
'widgets/RecommendationsPaintedDoorBtn/PaintedDoorExperimentContext',
() => 'PaintedDoorExperimentProvider',
);
let el;
const children = (<div>This is some <b>test</b> <i>content</i></div>);
const render = () => {
el = shallow(<AppWrapper>{children}</AppWrapper>);
};
const mockAndRenderForBlock = (newVal) => {
const oldVal = process.env;
beforeEach(() => {
process.env = { ...oldVal, ...newVal };
render();
});
afterEach(() => {
process.env = oldVal;
render();
});
};
describe('AppWrapper WidgetContainer component', () => {
describe('output', () => {
describe('painted door experiment is active (08/23)', () => {
mockAndRenderForBlock({ EXPERIMENT_08_23_VAN_PAINTED_DOOR: true });
test('snapshot', () => {
expect(el.snapshot).toMatchSnapshot();
});
it('renders children wrapped in PaintedDoorExperimentProvider', () => {
const control = el.instance.findByType(PaintedDoorExperimentProvider)[0];
expect(el.instance).toEqual(control);
});
});
describe('no experiments are active', () => {
mockAndRenderForBlock({ EXPERIMENT_08_23_VAN_PAINTED_DOOR: false });
test('snapshot', () => {
expect(el.snapshot).toMatchSnapshot();
});
it('renders children wrapped in PaintedDoorExperimentProvider', () => {
expect(el.instance.matches(shallow(children))).toEqual(true);
});
});
});
});

View File

@@ -1,8 +0,0 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`WidgetNavbar snapshots default 1`] = `
<RecommendationsPaintedDoorBtn
experimentVariation=""
placement="expendedNavbar"
/>
`;

View File

@@ -1,29 +0,0 @@
import React from 'react';
import PropTypes from 'prop-types';
import RecommendationsPaintedDoorBtn from 'widgets/RecommendationsPaintedDoorBtn';
import { COLLAPSED_NAVBAR, EXPANDED_NAVBAR } from 'widgets/RecommendationsPaintedDoorBtn/constants';
import {
usePaintedDoorExperimentContext,
} from 'widgets/RecommendationsPaintedDoorBtn/PaintedDoorExperimentContext';
export const WidgetNavbar = ({ placement }) => {
const {
experimentVariation,
isPaintedDoorNavbarBtnVariation,
experimentLoading,
} = usePaintedDoorExperimentContext();
if (!experimentLoading && isPaintedDoorNavbarBtnVariation) {
return (
<RecommendationsPaintedDoorBtn placement={placement} experimentVariation={experimentVariation} />
);
}
return null;
};
WidgetNavbar.propTypes = {
placement: PropTypes.oneOf([COLLAPSED_NAVBAR, EXPANDED_NAVBAR]).isRequired,
};
export default WidgetNavbar;

View File

@@ -1,64 +0,0 @@
import { shallow } from '@edx/react-unit-test-utils';
import {
usePaintedDoorExperimentContext,
} from '../../../widgets/RecommendationsPaintedDoorBtn/PaintedDoorExperimentContext';
import WidgetNavbar from './index';
import { EXPANDED_NAVBAR } from '../../../widgets/RecommendationsPaintedDoorBtn/constants';
jest.mock('widgets/RecommendationsPaintedDoorBtn/PaintedDoorExperimentContext', () => ({
usePaintedDoorExperimentContext: jest.fn(),
}));
describe('WidgetNavbar', () => {
let mockExperimentContext = {
experimentVariation: '',
isPaintedDoorNavbarBtnVariation: true,
experimentLoading: false,
};
const props = {
placement: EXPANDED_NAVBAR,
};
describe('snapshots', () => {
test('default', () => {
usePaintedDoorExperimentContext.mockReturnValueOnce(mockExperimentContext);
const wrapper = shallow(<WidgetNavbar {...props} />);
expect(usePaintedDoorExperimentContext).toHaveBeenCalled();
expect(wrapper.snapshot).toMatchSnapshot();
});
});
test('renders button if user in navbar variation', () => {
usePaintedDoorExperimentContext.mockReturnValueOnce(mockExperimentContext);
const wrapper = shallow(<WidgetNavbar {...props} />);
expect(usePaintedDoorExperimentContext).toHaveBeenCalled();
expect(wrapper.instance.type).toBe('RecommendationsPaintedDoorBtn');
});
test('renders nothing if user in not in navbar variation', () => {
mockExperimentContext = {
...mockExperimentContext,
isPaintedDoorNavbarBtnVariation: false,
};
usePaintedDoorExperimentContext.mockReturnValueOnce(mockExperimentContext);
const wrapper = shallow(<WidgetNavbar {...props} />);
expect(usePaintedDoorExperimentContext).toHaveBeenCalled();
expect(wrapper.shallowWrapper).toBeNull();
});
test('renders nothing if experiment is loading', () => {
mockExperimentContext = {
...mockExperimentContext,
isPaintedDoorNavbarBtnVariation: false,
experimentLoading: true,
};
usePaintedDoorExperimentContext.mockReturnValueOnce(mockExperimentContext);
const wrapper = shallow(<WidgetNavbar {...props} />);
expect(usePaintedDoorExperimentContext).toHaveBeenCalled();
expect(wrapper.shallowWrapper).toBeNull();
});
});

View File

@@ -1,6 +1,6 @@
import { StrictDict } from 'utils';
import { defineMessages } from '@edx/frontend-platform/i18n';
export const messages = StrictDict({
const messages = defineMessages({
loadingSR: {
id: 'learner-dash.loadingSR',
description: 'Page loading screen-reader text',

View File

@@ -1,129 +0,0 @@
import React from 'react';
import PropTypes from 'prop-types';
import { StrictDict } from 'utils';
import * as module from './PaintedDoorExperimentContext';
import {
useEmailConfirmationData,
useHasAvailableDashboards,
useRequestIsPending,
} from '../../data/redux/hooks';
import { RequestKeys } from '../../data/constants/requests';
import { trackPaintedDoorVariationGroup } from './track';
export const state = StrictDict({
enterpriseUser: (val) => React.useState(val), // eslint-disable-line
experimentData: (val) => React.useState(val), // eslint-disable-line
});
const PAINTED_DOOR_RECOMMENDATIONS_EXP_ID = 25116810832;
const PAINTED_DOOR_RECOMMENDATIONS_PAGE = 'url_targeting_for_van1604_recommendations_painted_door_exp';
const PAINTED_DOOR_RECS_EXP_NAVBAR_BTN_VARIATION = 'btn_navbar';
const PAINTED_DOOR_RECS_EXP_WIDGET_BTN_VARIATION = 'btn_widget';
const PAINTED_DOOR_RECS_EXP_CONTROL_VARIATION = 'control';
export function getPaintedDoorRecommendationsExperimentVariation() {
try {
if (window.optimizely && window.optimizely.get('data').experiments[PAINTED_DOOR_RECOMMENDATIONS_EXP_ID]) {
const selectedVariant = window.optimizely.get('state').getVariationMap()[PAINTED_DOOR_RECOMMENDATIONS_EXP_ID];
return selectedVariant?.name;
}
} catch (e) { /* empty */ }
return '';
}
export function activatePaintedDoorRecommendationsExperiment() {
window.optimizely = window.optimizely || [];
window.optimizely.push({
type: 'page',
pageName: PAINTED_DOOR_RECOMMENDATIONS_PAGE,
});
}
export const useIsEnterpriseUser = () => {
const [enterpriseUser, setEnterpriseUser] = module.state.enterpriseUser({
isEnterpriseUser: false,
isLoading: true,
});
const initIsPending = useRequestIsPending(RequestKeys.initialize);
const hasAvailableDashboards = useHasAvailableDashboards();
const confirmationData = useEmailConfirmationData();
React.useEffect(() => {
if (!initIsPending && Object.keys(confirmationData).length && hasAvailableDashboards) {
setEnterpriseUser(prev => ({
...prev,
isEnterpriseUser: true,
isLoading: false,
}));
} else if (!initIsPending && Object.keys(confirmationData).length && !hasAvailableDashboards) {
setEnterpriseUser(prev => ({
...prev,
isEnterpriseUser: false,
isLoading: false,
}));
}
}, [initIsPending]); // eslint-disable-line react-hooks/exhaustive-deps
return enterpriseUser;
};
export const PaintedDoorExperimentContext = React.createContext({
experimentVariation: null,
isPaintedDoorNavbarBtnVariation: null,
isPaintedDoorWidgetBtnVariation: null,
isPaintedDoorControlVariation: null,
experimentLoading: null,
});
export const PaintedDoorExperimentProvider = ({ children }) => {
const [experimentData, setExperimentData] = module.state.experimentData({
experimentVariation: '',
isPaintedDoorNavbarBtnVariation: false,
isPaintedDoorWidgetBtnVariation: false,
isPaintedDoorControlVariation: false,
experimentLoading: true,
});
const enterpriseUser = useIsEnterpriseUser();
const contextValue = React.useMemo(
() => ({
...experimentData,
}),
[experimentData],
);
React.useEffect(() => {
let timer = null;
if (!enterpriseUser.isLoading && !enterpriseUser.isEnterpriseUser) {
activatePaintedDoorRecommendationsExperiment();
timer = setTimeout(() => {
const variation = getPaintedDoorRecommendationsExperimentVariation();
setExperimentData(prev => ({
...prev,
experimentVariation: variation,
isPaintedDoorNavbarBtnVariation: variation === PAINTED_DOOR_RECS_EXP_NAVBAR_BTN_VARIATION,
isPaintedDoorWidgetBtnVariation: variation === PAINTED_DOOR_RECS_EXP_WIDGET_BTN_VARIATION,
isPaintedDoorControlVariation: variation === PAINTED_DOOR_RECS_EXP_CONTROL_VARIATION,
experimentLoading: false,
}));
trackPaintedDoorVariationGroup(variation);
}, 500);
}
return () => clearTimeout(timer);
}, [enterpriseUser]); // eslint-disable-line react-hooks/exhaustive-deps
return (
<PaintedDoorExperimentContext.Provider value={contextValue}>
{children}
</PaintedDoorExperimentContext.Provider>
);
};
export const usePaintedDoorExperimentContext = () => React.useContext(PaintedDoorExperimentContext);
PaintedDoorExperimentProvider.propTypes = {
children: PropTypes.node.isRequired,
};
export default PaintedDoorExperimentProvider;

View File

@@ -1,151 +0,0 @@
import React from 'react';
import { shallow } from '@edx/react-unit-test-utils';
import { MockUseState } from 'testUtils';
import * as experiment from './PaintedDoorExperimentContext';
import { useEmailConfirmationData, useHasAvailableDashboards, useRequestIsPending } from '../../data/redux/hooks';
import { trackPaintedDoorVariationGroup } from './track';
import { useIsEnterpriseUser } from './PaintedDoorExperimentContext';
const state = new MockUseState(experiment);
trackPaintedDoorVariationGroup();
jest.unmock('react');
jest.spyOn(React, 'useEffect').mockImplementation((cb, prereqs) => ({ useEffect: { cb, prereqs } }));
jest.mock('../../data/redux/hooks', () => ({
useRequestIsPending: jest.fn(),
useHasAvailableDashboards: jest.fn(),
useEmailConfirmationData: jest.fn(),
}));
jest.mock('./track', () => ({
trackPaintedDoorVariationGroup: jest.fn(),
}));
describe('useIsEnterpriseUser hook', () => {
describe('state fields', () => {
state.testGetter(state.keys.enterpriseUser);
});
describe('useIsEnterpriseUser', () => {
beforeEach(() => {
state.mock();
useRequestIsPending.mockReturnValueOnce(false);
useEmailConfirmationData.mockReturnValueOnce({ confirmed: true });
});
it('initializes enterpriseUser', () => {
useIsEnterpriseUser();
state.expectInitializedWith(state.keys.enterpriseUser, {
isEnterpriseUser: false,
isLoading: true,
});
});
it('get isEnterpriseUser false if useHasAvailableDashboards return false', () => {
useHasAvailableDashboards.mockReturnValueOnce(false);
state.mockVal(state.keys.enterpriseUser, {
isEnterpriseUser: false,
isLoading: false,
});
const enterpriseUser = useIsEnterpriseUser();
const [cb] = React.useEffect.mock.calls[0];
cb();
expect(enterpriseUser.isEnterpriseUser).toEqual(false);
expect(enterpriseUser.isLoading).toEqual(false);
});
it('get isEnterpriseUser true if useHasAvailableDashboards return true', () => {
useHasAvailableDashboards.mockReturnValueOnce(true);
state.mockVal(state.keys.enterpriseUser, {
isEnterpriseUser: true,
isLoading: false,
});
const enterpriseUser = useIsEnterpriseUser();
const [cb] = React.useEffect.mock.calls[0];
cb();
expect(enterpriseUser.isEnterpriseUser).toEqual(true);
expect(enterpriseUser.isLoading).toEqual(false);
});
});
});
describe('Painted door experiments context', () => {
beforeEach(() => {
jest.resetAllMocks();
});
describe('PaintedDoorExperimentProvider', () => {
const { PaintedDoorExperimentProvider } = experiment;
const TestComponent = () => {
const {
experimentVariation,
isPaintedDoorNavbarBtnVariation,
isPaintedDoorWidgetBtnVariation,
isPaintedDoorControlVariation,
experimentLoading,
} = experiment.usePaintedDoorExperimentContext();
expect(experimentVariation).toEqual('');
expect(isPaintedDoorNavbarBtnVariation).toBe(false);
expect(isPaintedDoorWidgetBtnVariation).toBe(false);
expect(isPaintedDoorControlVariation).toBe(false);
expect(experimentLoading).toBe(true);
return (
<div />
);
};
it('test experiment gets activated for non enterprise users', () => {
state.mock();
jest.useFakeTimers();
useRequestIsPending.mockReturnValueOnce(false);
useHasAvailableDashboards.mockReturnValueOnce(false);
useEmailConfirmationData.mockReturnValueOnce({ confirmed: true });
state.mockVal(state.keys.enterpriseUser, {
isEnterpriseUser: false,
isLoading: false,
});
shallow(
<PaintedDoorExperimentProvider>
<TestComponent />
</PaintedDoorExperimentProvider>,
);
const [cb] = React.useEffect.mock.calls[1];
cb();
jest.advanceTimersByTime(500);
expect(trackPaintedDoorVariationGroup).toHaveBeenCalledTimes(1);
});
it('test experiment does not get activated for enterprise users', () => {
state.mock();
jest.useFakeTimers();
useRequestIsPending.mockReturnValueOnce(false);
useHasAvailableDashboards.mockReturnValueOnce(true);
useEmailConfirmationData.mockReturnValueOnce({ confirmed: true });
state.mockVal(state.keys.enterpriseUser, {
isEnterpriseUser: true,
isLoading: false,
});
shallow(
<PaintedDoorExperimentProvider>
<TestComponent />
</PaintedDoorExperimentProvider>,
);
const [cb] = React.useEffect.mock.calls[1];
cb();
jest.advanceTimersByTime(500);
expect(trackPaintedDoorVariationGroup).toHaveBeenCalledTimes(0);
});
});
});

View File

@@ -1,14 +0,0 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`RecommendationsPaintedDoorBtn matches snapshot 1`] = `
<Fragment>
<RecommendationsPanelButton
handleClick={[Function]}
/>
<ModalView
isOpen={false}
onClose={[MockFunction]}
variation=""
/>
</Fragment>
`;

View File

@@ -1,31 +0,0 @@
import React from 'react';
import { Button } from '@openedx/paragon';
import { useIntl } from '@edx/frontend-platform/i18n';
import PropTypes from 'prop-types';
import classNames from 'classnames';
import messages from '../messages';
import { COLLAPSED_NAVBAR, EXPANDED_NAVBAR } from '../constants';
export const NavbarButton = ({ placement, handleClick }) => {
const { formatMessage } = useIntl();
return (
<Button
as="a"
className={classNames({
'p-4': placement === EXPANDED_NAVBAR,
})}
variant="inverse-primary"
onClick={handleClick}
>
{formatMessage(messages.recommendedForYou)}
</Button>
);
};
NavbarButton.propTypes = {
placement: PropTypes.oneOf([COLLAPSED_NAVBAR, EXPANDED_NAVBAR]).isRequired,
handleClick: PropTypes.func.isRequired,
};
export default NavbarButton;

View File

@@ -1,52 +0,0 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`ModalView snapshot should renders default ModalView 1`] = `
<div
className="containers modal-container"
>
<[object Object]
hasCloseButton={false}
isBlocking={true}
isFullscreenScroll={true}
isOpen={true}
onClose={[MockFunction]}
title=""
>
<ModalDialog.Header>
<ModalDialog.Title
className="mt-2"
>
Thank you for your interest!
</ModalDialog.Title>
</ModalDialog.Header>
<ModalDialog.Body>
<div>
<p
className="mt-2"
>
Personalized recommendations feature is not yet available. We are working hard on bringing it to your learner home in the near future.
</p>
<p>
Would you like to be alerted when it becomes available?
</p>
</div>
</ModalDialog.Body>
<ModalDialog.Footer>
<ActionRow>
<ModalDialog.CloseButton
onClick={[Function]}
variant="tertiary"
>
Skip for now
</ModalDialog.CloseButton>
<ModalDialog.CloseButton
onClick={[Function]}
variant="primary"
>
Count me in!
</ModalDialog.CloseButton>
</ActionRow>
</ModalDialog.Footer>
</[object Object]>
</div>
`;

View File

@@ -1,18 +0,0 @@
import React from 'react';
import { StrictDict } from 'utils';
import * as module from './hooks';
export const state = StrictDict({
isModalOpen: (val) => React.useState(val), // eslint-disable-line
});
export const usePaintedDoorModal = () => {
const [isModalOpen, setIsModalOpen] = module.state.isModalOpen(false);
const toggleModal = () => setIsModalOpen(!isModalOpen);
return {
isModalOpen,
toggleModal,
};
};

View File

@@ -1,30 +0,0 @@
import { MockUseState } from 'testUtils';
import * as hooks from './hooks';
const state = new MockUseState(hooks);
const {
usePaintedDoorModal,
} = hooks;
describe('LearnerDashboardHeader hooks', () => {
describe('state fields', () => {
state.testGetter(state.keys.isModalOpen);
});
describe('useRecommendationsModal', () => {
beforeEach(() => {
state.mock();
});
it('initializes isModalOpen with false', () => {
usePaintedDoorModal();
state.expectInitializedWith(state.keys.isModalOpen, false);
});
test('change isModalOpen value on toggle', () => {
const out = usePaintedDoorModal();
state.expectInitializedWith(state.keys.isModalOpen, false);
out.toggleModal();
expect(state.values.isModalOpen).toEqual(true);
});
});
});

View File

@@ -1,70 +0,0 @@
import React from 'react';
import PropTypes from 'prop-types';
import { ModalDialog, ActionRow } from '@openedx/paragon';
import { useIntl } from '@edx/frontend-platform/i18n';
import messages from '../../messages';
import './index.scss';
import {
trackPaintedDoorRecommendationHomeInterestBtnClicked,
trackPaintedDoorRecommendationHomeSkipBtnClicked,
} from '../../track';
export const ModalView = ({
isOpen,
onClose,
variation,
}) => {
const { formatMessage } = useIntl();
const handleSkipBtnClick = () => trackPaintedDoorRecommendationHomeSkipBtnClicked(variation);
const handleInterestBtnClick = () => trackPaintedDoorRecommendationHomeInterestBtnClicked(variation);
return (
<div className="containers modal-container">
<ModalDialog
title=""
isOpen={isOpen}
onClose={onClose}
hasCloseButton={false}
isFullscreenScroll
isBlocking
>
<ModalDialog.Header>
<ModalDialog.Title className="mt-2">
{formatMessage(messages.recommendationsModalHeading)}
</ModalDialog.Title>
</ModalDialog.Header>
<ModalDialog.Body>
<div>
<p className="mt-2">{formatMessage(messages.recommendationsFeatureText)}</p>
<p>{formatMessage(messages.recommendationsAlertText)}</p>
</div>
</ModalDialog.Body>
<ModalDialog.Footer>
<ActionRow>
<ModalDialog.CloseButton variant="tertiary" onClick={handleSkipBtnClick}>
{formatMessage(messages.modalSkipButton)}
</ModalDialog.CloseButton>
<ModalDialog.CloseButton variant="primary" onClick={handleInterestBtnClick}>
{formatMessage(messages.modalCountMeButton)}
</ModalDialog.CloseButton>
</ActionRow>
</ModalDialog.Footer>
</ModalDialog>
</div>
);
};
ModalView.defaultProps = {
isOpen: false,
};
ModalView.propTypes = {
onClose: PropTypes.func.isRequired,
isOpen: PropTypes.bool,
variation: PropTypes.string.isRequired,
};
export default ModalView;

View File

@@ -1,15 +0,0 @@
@import "@openedx/paragon/scss/core/core";
.modal-container {
display: none;
}
@media (max-width: 464px) {
.pgn__action-row{
flex-direction: column;
gap: 5px;
button {
width: 100%;
}
}
}

View File

@@ -1,33 +0,0 @@
import { shallow } from '@edx/react-unit-test-utils';
import ModalView from '.';
jest.mock('../../track', () => ({
trackPaintedDoorRecommendationHomeSkipBtnClicked: jest.fn(),
trackPaintedDoorRecommendationHomeInterestBtnClicked: jest.fn(),
}));
jest.mock('@openedx/paragon', () => ({
...jest.requireActual('@openedx/paragon'),
ModalDialog: {
Body: 'ModalDialog.Body',
Header: 'ModalDialog.Header',
Title: 'ModalDialog.Title',
Footer: 'ModalDialog.Footer',
CloseButton: 'ModalDialog.CloseButton',
},
ActionRow: 'ActionRow',
}));
describe('ModalView', () => {
const props = {
isOpen: true,
onClose: jest.fn(),
variation: '',
};
describe('snapshot', () => {
test('should renders default ModalView', () => {
const wrapper = shallow(<ModalView {...props} />);
expect(wrapper.snapshot).toMatchSnapshot();
});
});
});

View File

@@ -1,25 +0,0 @@
import React from 'react';
import { Button } from '@openedx/paragon';
import { useIntl } from '@edx/frontend-platform/i18n';
import PropTypes from 'prop-types';
import messages from '../messages';
export const RecommendationsPanelButton = ({ handleClick }) => {
const { formatMessage } = useIntl();
return (
<Button
as="a"
variant="brand"
onClick={handleClick}
>
{formatMessage(messages.seeAllRecommendationsButton)}
</Button>
);
};
RecommendationsPanelButton.propTypes = {
handleClick: PropTypes.func.isRequired,
};
export default RecommendationsPanelButton;

View File

@@ -1,3 +0,0 @@
export const COLLAPSED_NAVBAR = 'collapsedNavbar';
export const EXPANDED_NAVBAR = 'expendedNavbar';
export const RECOMMENDATIONS_PANEL = 'recommendationsPanel';

View File

@@ -1,44 +0,0 @@
import React from 'react';
import PropTypes from 'prop-types';
import NavbarButton from './components/NavbarButton';
import PaintedDoorModal from './components/PaintedDoorModal';
import { usePaintedDoorModal } from './components/PaintedDoorModal/hooks';
import { COLLAPSED_NAVBAR, EXPANDED_NAVBAR, RECOMMENDATIONS_PANEL } from './constants';
import RecommendationsPanelButton from './components/RecommendationsPanelButton';
import { trackPaintedDoorRecommendationHomeBtnClicked } from './track';
export const RecommendationsPaintedDoorBtn = ({ placement, experimentVariation }) => {
const { isModalOpen, toggleModal } = usePaintedDoorModal();
const handleClick = () => {
toggleModal();
trackPaintedDoorRecommendationHomeBtnClicked(experimentVariation);
};
const getPaintedDoorButton = () => {
if ([COLLAPSED_NAVBAR, EXPANDED_NAVBAR].includes(placement)) {
return (
<NavbarButton handleClick={handleClick} placement={placement} />
);
} if (placement === RECOMMENDATIONS_PANEL) {
return (
<RecommendationsPanelButton handleClick={handleClick} />
);
}
return null;
};
return (
<>
{getPaintedDoorButton()}
<PaintedDoorModal isOpen={isModalOpen} onClose={toggleModal} variation={experimentVariation} />
</>
);
};
RecommendationsPaintedDoorBtn.propTypes = {
placement: PropTypes.oneOf([COLLAPSED_NAVBAR, EXPANDED_NAVBAR, RECOMMENDATIONS_PANEL]).isRequired,
experimentVariation: PropTypes.string.isRequired,
};
export default RecommendationsPaintedDoorBtn;

View File

@@ -1,95 +0,0 @@
import React from 'react';
import { shallow } from '@edx/react-unit-test-utils';
import { ModalDialog } from '@openedx/paragon';
import RecommendationsPaintedDoorBtn from './index';
import { EXPANDED_NAVBAR, RECOMMENDATIONS_PANEL } from './constants';
import NavbarButton from './components/NavbarButton';
import RecommendationsPanelButton from './components/RecommendationsPanelButton';
import { trackPaintedDoorRecommendationHomeBtnClicked } from './track';
jest.mock('./components/PaintedDoorModal/hooks', () => ({
usePaintedDoorModal: jest.fn(() => ({
isModalOpen: false,
toggleModal: jest.fn(),
})),
}));
jest.mock('./track', () => ({
trackPaintedDoorRecommendationHomeBtnClicked: jest.fn(),
}));
describe('RecommendationsPaintedDoorBtn', () => {
let props = {
placement: RECOMMENDATIONS_PANEL,
experimentVariation: '',
};
it('matches snapshot', () => {
expect(shallow(<RecommendationsPaintedDoorBtn {...props} />).snapshot).toMatchSnapshot();
});
it('renders painted door modal', () => {
const wrapper = shallow(<RecommendationsPaintedDoorBtn {...props} />);
expect(wrapper.instance.findByType(ModalDialog)).toBeTruthy();
});
it('renders painted door navbar button', () => {
props = {
...props,
placement: EXPANDED_NAVBAR,
};
const wrapper = shallow(<RecommendationsPaintedDoorBtn {...props} />);
expect(wrapper.instance.findByType(NavbarButton)).not.toHaveLength(0);
expect(wrapper.instance.findByType(RecommendationsPanelButton)).toHaveLength(0);
});
it('renders painted door recommendations panel button', () => {
props = {
...props,
placement: RECOMMENDATIONS_PANEL,
};
const wrapper = shallow(<RecommendationsPaintedDoorBtn {...props} />);
expect(wrapper.instance.findByType(NavbarButton)).toHaveLength(0);
expect(wrapper.instance.findByType(RecommendationsPanelButton)).not.toHaveLength(0);
});
it('test no button (null) rendered for invalid placement', () => {
props = {
...props,
placement: '',
};
const wrapper = shallow(<RecommendationsPaintedDoorBtn {...props} />);
expect(wrapper.instance.findByType(NavbarButton)).toHaveLength(0);
expect(wrapper.instance.findByType(RecommendationsPanelButton)).toHaveLength(0);
});
it('test track event is fired on navbar button click', () => {
props = {
...props,
placement: EXPANDED_NAVBAR,
};
const wrapper = shallow(<RecommendationsPaintedDoorBtn {...props} />);
const navbarButton = wrapper.instance.findByType(NavbarButton)[0];
navbarButton.props.handleClick();
expect(trackPaintedDoorRecommendationHomeBtnClicked).toHaveBeenCalled();
});
it('test track event is fired on recommendations panel button click', () => {
props = {
...props,
placement: RECOMMENDATIONS_PANEL,
};
const wrapper = shallow(<RecommendationsPaintedDoorBtn {...props} />);
const recommendationsPanelButton = wrapper.instance.findByType(RecommendationsPanelButton)[0];
recommendationsPanelButton.props.handleClick();
expect(trackPaintedDoorRecommendationHomeBtnClicked).toHaveBeenCalled();
});
});

View File

@@ -1,41 +0,0 @@
import { defineMessages } from '@edx/frontend-platform/i18n';
const messages = defineMessages({
recommendationsFeatureText: {
id: 'RecommendationsPanel.recommendationsFeatureText',
defaultMessage: 'Personalized recommendations feature is not yet available. We are working hard on bringing it to your learner home in the near future.',
description: 'recommendations feature text',
},
recommendationsAlertText: {
id: 'RecommendationsPanel.recommendationsAlertText',
defaultMessage: 'Would you like to be alerted when it becomes available?',
description: 'recommendations alerted text',
},
recommendationsModalHeading: {
id: 'RecommendationsPanel.recommendationsModalHeading',
defaultMessage: 'Thank you for your interest!',
description: 'Heading of modal',
},
modalSkipButton: {
id: 'RecommendationsPanel.modalSkipButton',
defaultMessage: 'Skip for now',
description: 'button for Skip for now',
},
modalCountMeButton: {
id: 'RecommendationsPanel.modalCountMeButton',
defaultMessage: 'Count me in!',
description: 'button for Count me in!',
},
recommendedForYou: {
id: 'learnerVariantDashboard.recommendedForYou',
defaultMessage: 'Recommended For You',
description: 'Header link for recommended page.',
},
seeAllRecommendationsButton: {
id: 'RecommendationsPanel.seeAllRecommendationsButton',
defaultMessage: 'See All Recommendations',
description: 'Button to see all recommendations',
},
});
export default messages;

View File

@@ -1,35 +0,0 @@
import { StrictDict } from 'utils';
import { createEventTracker } from 'data/services/segment/utils';
export const eventNames = StrictDict({
variationGroup: 'edx.bi.user.recommendation_home.variation.group',
recommendationHomeBtnClicked: 'edx.bi.user.recommendation_home.btn.clicked',
recommendationHomeModalInterestBtnClicked: 'edx.bi.user.recommendation_home.modal.interest_btn.clicked',
recommendationHomeModalSkipBtnClicked: 'edx.bi.user.recommendation_home.modal.skip_btn.clicked',
});
export const trackPaintedDoorVariationGroup = (variation) => {
createEventTracker(eventNames.variationGroup, {
variation,
page: 'dashboard',
})();
};
export const trackPaintedDoorRecommendationHomeBtnClicked = (variation) => {
createEventTracker(eventNames.recommendationHomeBtnClicked, {
variation,
page: 'dashboard',
})();
};
export const trackPaintedDoorRecommendationHomeInterestBtnClicked = (variation) => {
createEventTracker(eventNames.recommendationHomeModalInterestBtnClicked, {
variation,
})();
};
export const trackPaintedDoorRecommendationHomeSkipBtnClicked = (variation) => {
createEventTracker(eventNames.recommendationHomeModalSkipBtnClicked, {
variation,
})();
};

View File

@@ -1,58 +0,0 @@
import { createEventTracker } from 'data/services/segment/utils';
import {
eventNames,
trackPaintedDoorRecommendationHomeBtnClicked,
trackPaintedDoorVariationGroup,
trackPaintedDoorRecommendationHomeSkipBtnClicked,
trackPaintedDoorRecommendationHomeInterestBtnClicked,
} from './track';
jest.mock('data/services/segment/utils', () => ({
createEventTracker: jest.fn(() => () => {}),
}));
const TEST_VARIATION = 'testVariation';
describe('Recommendations Painted Door experiment trackers', () => {
it('test creates an event tracker for painted door variation group', () => {
trackPaintedDoorVariationGroup(TEST_VARIATION);
expect(createEventTracker).toHaveBeenCalledWith(
eventNames.variationGroup,
{
variation: TEST_VARIATION,
page: 'dashboard',
},
);
});
it('test creates an event tracker for painted door button click', () => {
trackPaintedDoorRecommendationHomeBtnClicked(TEST_VARIATION);
expect(createEventTracker).toHaveBeenCalledWith(
eventNames.recommendationHomeBtnClicked,
{
variation: TEST_VARIATION,
page: 'dashboard',
},
);
});
it('test creates an event tracker for painted door skip button click', () => {
trackPaintedDoorRecommendationHomeSkipBtnClicked(TEST_VARIATION);
expect(createEventTracker).toHaveBeenCalledWith(
eventNames.recommendationHomeModalSkipBtnClicked,
{
variation: TEST_VARIATION,
},
);
});
it('test creates an event tracker for painted door interest button click', () => {
trackPaintedDoorRecommendationHomeInterestBtnClicked(TEST_VARIATION);
expect(createEventTracker).toHaveBeenCalledWith(
eventNames.recommendationHomeModalInterestBtnClicked,
{
variation: TEST_VARIATION,
},
);
});
});

View File

@@ -12,9 +12,6 @@ import CourseCard from './components/CourseCard';
import messages from './messages';
import './index.scss';
import { usePaintedDoorExperimentContext } from '../RecommendationsPaintedDoorBtn/PaintedDoorExperimentContext';
import { RECOMMENDATIONS_PANEL } from '../RecommendationsPaintedDoorBtn/constants';
import RecommendationsPaintedDoorBtn from '../RecommendationsPaintedDoorBtn';
export const LoadedView = ({
courses,
@@ -22,11 +19,6 @@ export const LoadedView = ({
}) => {
const { formatMessage } = useIntl();
const { courseSearchUrl } = reduxHooks.usePlatformSettingsData();
const {
experimentVariation,
isPaintedDoorWidgetBtnVariation,
experimentLoading,
} = usePaintedDoorExperimentContext();
return (
<div className="p-4 w-100 panel-background">
@@ -43,19 +35,15 @@ export const LoadedView = ({
))}
</div>
<div className="text-center explore-courses-btn">
{!experimentLoading && isPaintedDoorWidgetBtnVariation ? (
<RecommendationsPaintedDoorBtn placement={RECOMMENDATIONS_PANEL} experimentVariation={experimentVariation} />
) : (
<Button
variant="tertiary"
iconBefore={Search}
as="a"
href={baseAppUrl(courseSearchUrl)}
onClick={track.findCoursesWidgetClicked(baseAppUrl(courseSearchUrl))}
>
{formatMessage(messages.exploreCoursesButton)}
</Button>
)}
<Button
variant="tertiary"
iconBefore={Search}
as="a"
href={baseAppUrl(courseSearchUrl)}
onClick={track.findCoursesWidgetClicked(baseAppUrl(courseSearchUrl))}
>
{formatMessage(messages.exploreCoursesButton)}
</Button>
</div>
</div>
);

View File

@@ -1,12 +1,9 @@
import React from 'react';
import { shallow } from '@edx/react-unit-test-utils';
import { Button } from '@openedx/paragon';
import LoadedView from './LoadedView';
import mockData from './mockData';
import messages from './messages';
import { usePaintedDoorExperimentContext } from '../RecommendationsPaintedDoorBtn/PaintedDoorExperimentContext';
import RecommendationsPaintedDoorBtn from '../RecommendationsPaintedDoorBtn';
jest.mock('hooks', () => ({
reduxHooks: {
@@ -22,64 +19,24 @@ jest.mock('./track', () => ({
findCoursesWidgetClicked: (href) => jest.fn().mockName(`track.findCoursesWidgetClicked('${href}')`),
}));
jest.mock('./components/CourseCard', () => 'CourseCard');
jest.mock('widgets/RecommendationsPaintedDoorBtn/PaintedDoorExperimentContext', () => ({
usePaintedDoorExperimentContext: jest.fn(),
}));
describe('RecommendationsPanel LoadedView', () => {
const props = {
courses: mockData.courses,
isControl: null,
};
let mockExperimentContext = {
experimentVariation: '',
isPaintedDoorWidgetBtnVariation: true,
experimentLoading: false,
};
describe('RecommendationPanelLoadedView', () => {
test('without personalize recommendation', () => {
usePaintedDoorExperimentContext.mockReturnValueOnce(mockExperimentContext);
const el = shallow(<LoadedView {...props} />);
expect(el.snapshot).toMatchSnapshot();
expect(el.instance.findByType('h3')[0].children[0].el).toEqual(messages.popularCoursesHeading.defaultMessage);
});
test('with personalize recommendation', () => {
usePaintedDoorExperimentContext.mockReturnValueOnce(mockExperimentContext);
const el = shallow(<LoadedView {...props} isControl={false} />);
expect(el.snapshot).toMatchSnapshot();
expect(el.instance.findByType('h3')[0].children[0].el).toEqual(messages.recommendationsHeading.defaultMessage);
});
test('test painted door button is rendered if user is in variation', () => {
usePaintedDoorExperimentContext.mockReturnValueOnce(mockExperimentContext);
const wrapper = shallow(<LoadedView {...props} />);
expect(wrapper.instance.findByType(RecommendationsPaintedDoorBtn)).not.toHaveLength(0);
});
test('test explore courses button is returned if user is not in variation', () => {
mockExperimentContext = {
...mockExperimentContext,
isPaintedDoorWidgetBtnVariation: false,
};
usePaintedDoorExperimentContext.mockReturnValueOnce(mockExperimentContext);
const wrapper = shallow(<LoadedView {...props} />);
expect(wrapper.instance.findByType(RecommendationsPaintedDoorBtn)).toHaveLength(0);
expect(wrapper.instance.findByType(Button)[0].children[0].el)
.toEqual(messages.exploreCoursesButton.defaultMessage);
});
test('test explore courses button is returned if experiment is loading', () => {
mockExperimentContext = {
...mockExperimentContext,
isPaintedDoorWidgetBtnVariation: false,
experimentLoading: true,
};
usePaintedDoorExperimentContext.mockReturnValueOnce(mockExperimentContext);
const wrapper = shallow(<LoadedView {...props} />);
expect(wrapper.instance.findByType(RecommendationsPaintedDoorBtn)).toHaveLength(0);
expect(wrapper.instance.findByType(Button)[0].children[0].el)
.toEqual(messages.exploreCoursesButton.defaultMessage);
});
});
});

View File

@@ -62,10 +62,15 @@ exports[`RecommendationsPanel LoadedView RecommendationPanelLoadedView with pers
<div
className="text-center explore-courses-btn"
>
<RecommendationsPaintedDoorBtn
experimentVariation=""
placement="recommendationsPanel"
/>
<Button
as="a"
href="http://localhost:18000/course-search-url"
iconBefore={[MockFunction icons.Search]}
onClick={[MockFunction track.findCoursesWidgetClicked('http://localhost:18000/course-search-url')]}
variant="tertiary"
>
Explore courses
</Button>
</div>
</div>
`;
@@ -132,10 +137,15 @@ exports[`RecommendationsPanel LoadedView RecommendationPanelLoadedView without p
<div
className="text-center explore-courses-btn"
>
<RecommendationsPaintedDoorBtn
experimentVariation=""
placement="recommendationsPanel"
/>
<Button
as="a"
href="http://localhost:18000/course-search-url"
iconBefore={[MockFunction icons.Search]}
onClick={[MockFunction track.findCoursesWidgetClicked('http://localhost:18000/course-search-url')]}
variant="tertiary"
>
Explore courses
</Button>
</div>
</div>
`;

View File

@@ -4,9 +4,6 @@ import LookingForChallengeWidget from 'widgets/LookingForChallengeWidget';
import LoadingView from './LoadingView';
import LoadedView from './LoadedView';
import hooks from './hooks';
import RecommendationsPaintedDoorBtn from '../RecommendationsPaintedDoorBtn';
import { RECOMMENDATIONS_PANEL } from '../RecommendationsPaintedDoorBtn/constants';
import { usePaintedDoorExperimentContext } from '../RecommendationsPaintedDoorBtn/PaintedDoorExperimentContext';
export const RecommendationsPanel = () => {
const {
@@ -16,29 +13,6 @@ export const RecommendationsPanel = () => {
isLoaded,
isLoading,
} = hooks.useRecommendationPanelData();
const {
experimentVariation,
isPaintedDoorWidgetBtnVariation,
experimentLoading,
} = usePaintedDoorExperimentContext();
const getDefaultOrFailedStateWidget = () => {
if (!experimentLoading && isPaintedDoorWidgetBtnVariation) {
return (
<>
<LookingForChallengeWidget />
<div className="pt-3" />
<RecommendationsPaintedDoorBtn
experimentVariation={experimentVariation}
placement={RECOMMENDATIONS_PANEL}
/>
</>
);
}
return (
<LookingForChallengeWidget />
);
};
if (isLoading) {
return (<LoadingView />);
@@ -49,10 +23,10 @@ export const RecommendationsPanel = () => {
);
}
if (isFailed) {
return getDefaultOrFailedStateWidget();
return (<LookingForChallengeWidget />);
}
// default fallback
return getDefaultOrFailedStateWidget();
return (<LookingForChallengeWidget />);
};
export default RecommendationsPanel;

View File

@@ -7,8 +7,6 @@ import mockData from './mockData';
import LoadedView from './LoadedView';
import LoadingView from './LoadingView';
import RecommendationsPanel from '.';
import { usePaintedDoorExperimentContext } from '../RecommendationsPaintedDoorBtn/PaintedDoorExperimentContext';
import RecommendationsPaintedDoorBtn from '../RecommendationsPaintedDoorBtn';
jest.mock('./hooks', () => ({
useRecommendationPanelData: jest.fn(),
@@ -16,9 +14,6 @@ jest.mock('./hooks', () => ({
jest.mock('widgets/LookingForChallengeWidget', () => 'LookingForChallengeWidget');
jest.mock('./LoadingView', () => 'LoadingView');
jest.mock('./LoadedView', () => 'LoadedView');
jest.mock('widgets/RecommendationsPaintedDoorBtn/PaintedDoorExperimentContext', () => ({
usePaintedDoorExperimentContext: jest.fn(),
}));
const { courses } = mockData;
@@ -34,13 +29,6 @@ describe('RecommendationsPanel snapshot', () => {
...defaultLoadedViewProps,
};
describe('RecommendationsPanel recommendations tests', () => {
beforeEach(() => {
usePaintedDoorExperimentContext.mockReturnValueOnce({
experimentVariation: '',
isPaintedDoorWidgetBtnVariation: false,
experimentLoading: false,
});
});
it('displays LoadingView if request is loading', () => {
hooks.useRecommendationPanelData.mockReturnValueOnce({
...defaultValues,
@@ -77,65 +65,4 @@ describe('RecommendationsPanel snapshot', () => {
);
});
});
describe('RecommendationsPanel painted door exp tests', () => {
it('displays painted door btn if user is in variation and request is failed', () => {
hooks.useRecommendationPanelData.mockReturnValueOnce({
...defaultValues,
isFailed: true,
});
usePaintedDoorExperimentContext.mockReturnValueOnce({
experimentVariation: '',
isPaintedDoorWidgetBtnVariation: true,
experimentLoading: false,
});
const wrapper = shallow(<RecommendationsPanel />);
expect(wrapper.instance.findByType(RecommendationsPaintedDoorBtn)).not.toHaveLength(0);
});
it('displays painted door btn if user is in variation and no flags are set (defaults)', () => {
hooks.useRecommendationPanelData.mockReturnValueOnce({
...defaultValues,
isFailed: true,
});
usePaintedDoorExperimentContext.mockReturnValueOnce({
experimentVariation: '',
isPaintedDoorWidgetBtnVariation: true,
experimentLoading: false,
});
const wrapper = shallow(<RecommendationsPanel />);
expect(wrapper.instance.findByType(RecommendationsPaintedDoorBtn)).not.toHaveLength(0);
});
it('renders only LookingForChallengeWidget if user is not in variation', () => {
hooks.useRecommendationPanelData.mockReturnValueOnce({
...defaultValues,
isFailed: true,
});
usePaintedDoorExperimentContext.mockReturnValueOnce({
experimentVariation: '',
isPaintedDoorWidgetBtnVariation: false,
experimentLoading: false,
});
expect({ ...shallow(<RecommendationsPanel />).shallowWrapper, children: expect.any(Array) }).toMatchObject(
shallow(<LookingForChallengeWidget />),
);
});
it('renders only LookingForChallengeWidget if experiment is loading', () => {
hooks.useRecommendationPanelData.mockReturnValueOnce({
...defaultValues,
isFailed: true,
});
usePaintedDoorExperimentContext.mockReturnValueOnce({
experimentVariation: '',
isPaintedDoorWidgetBtnVariation: false,
experimentLoading: true,
});
expect({ ...shallow(<RecommendationsPanel />).shallowWrapper, children: expect.any(Array) }).toMatchObject(
shallow(<LookingForChallengeWidget />),
);
});
});
});