From 1425dbb6cf0788ee36137ae1622c12a1e0f2e433 Mon Sep 17 00:00:00 2001 From: Kira Miller Date: Fri, 29 Mar 2024 00:06:04 +0000 Subject: [PATCH 1/2] fix: hiding highlights for custom courses --- src/components/Sidebar/index.jsx | 28 +++++++++++++++-- src/containers/Sidebar/Sidebar.test.jsx | 41 +++++++++++++++++++++++-- src/containers/Sidebar/index.jsx | 1 + src/data/services/LmsApiService.js | 5 +++ 4 files changed, 71 insertions(+), 4 deletions(-) diff --git a/src/components/Sidebar/index.jsx b/src/components/Sidebar/index.jsx index 3d73d601d3..e32ba84681 100644 --- a/src/components/Sidebar/index.jsx +++ b/src/components/Sidebar/index.jsx @@ -1,5 +1,5 @@ import React, { - useRef, useContext, useEffect, useCallback, + useCallback, useContext, useEffect, useRef, useState, } from 'react'; import PropTypes from 'prop-types'; import classNames from 'classnames'; @@ -8,6 +8,7 @@ import { BookOpen, CreditCard, Description, InsertChartOutlined, MoneyOutline, Settings, Support, Tag, TrendingUp, } from '@edx/paragon/icons'; +import { logError } from '@edx/frontend-platform/logging'; import { getConfig } from '@edx/frontend-platform/config'; import IconLink from './IconLink'; @@ -18,6 +19,7 @@ import { TOUR_TARGETS } from '../ProductTours/constants'; import { useOnMount } from '../../hooks'; import { EnterpriseSubsidiesContext } from '../EnterpriseSubsidiesContext'; import { EnterpriseAppContext } from '../EnterpriseApp/EnterpriseAppContextProvider'; +import LmsApiService from '../../data/services/LmsApiService'; const Sidebar = ({ baseUrl, @@ -31,6 +33,7 @@ const Sidebar = ({ enableAnalyticsScreen, onWidthChange, isMobile, + enterpriseGroupsV1, }) => { const navRef = useRef(); const widthRef = useRef(); @@ -38,6 +41,8 @@ const Sidebar = ({ const { subsidyRequestsCounts } = useContext(SubsidyRequestsContext); const { canManageLearnerCredit } = useContext(EnterpriseSubsidiesContext); const { FEATURE_CONTENT_HIGHLIGHTS } = getConfig(); + const [isSubGroup, setIsSubGroup] = useState(false); + const hideHighlightsForGroups = enterpriseGroupsV1 && isSubGroup; const getSidebarWidth = useCallback(() => { if (navRef && navRef.current) { @@ -54,6 +59,23 @@ const Sidebar = ({ widthRef.current = sideBarWidth; onWidthChange(sideBarWidth); } + async function fetchGroupsData() { + try { + const response = await LmsApiService.fetchEnterpriseGroup(); + // we only want to hide the feature if a customer has a group this does not + // apply to all contexts/include all users + response.data.results.forEach((group) => { + if (group.applies_to_all_contexts === false) { + setIsSubGroup(true); + } + }); + } catch (error) { + logError(error); + } + } + if (enterpriseGroupsV1) { + fetchGroupsData(); + } }); useEffect(() => { @@ -104,7 +126,7 @@ const Sidebar = ({ id: TOUR_TARGETS.CONTENT_HIGHLIGHTS, to: `${baseUrl}/admin/${ROUTE_NAMES.contentHighlights}`, icon: , - hidden: !FEATURE_CONTENT_HIGHLIGHTS || !enterpriseCuration?.isHighlightFeatureActive, + hidden: !FEATURE_CONTENT_HIGHLIGHTS || !enterpriseCuration?.isHighlightFeatureActive || hideHighlightsForGroups, notification: isNewArchivedContent, }, { @@ -187,6 +209,7 @@ Sidebar.defaultProps = { enableAnalyticsScreen: false, onWidthChange: () => {}, isMobile: false, + enterpriseGroupsV1: false, }; Sidebar.propTypes = { @@ -201,6 +224,7 @@ Sidebar.propTypes = { enableAnalyticsScreen: PropTypes.bool, onWidthChange: PropTypes.func, isMobile: PropTypes.bool, + enterpriseGroupsV1: PropTypes.bool, }; export default Sidebar; diff --git a/src/containers/Sidebar/Sidebar.test.jsx b/src/containers/Sidebar/Sidebar.test.jsx index 821b758430..fff6d2471e 100644 --- a/src/containers/Sidebar/Sidebar.test.jsx +++ b/src/containers/Sidebar/Sidebar.test.jsx @@ -1,6 +1,5 @@ /* eslint-disable react/prop-types */ import React from 'react'; -import { getConfig } from '@edx/frontend-platform/config'; import PropTypes from 'prop-types'; import configureMockStore from 'redux-mock-store'; import thunk from 'redux-thunk'; @@ -12,12 +11,13 @@ import { render, screen, } from '@testing-library/react'; import '@testing-library/jest-dom'; +import { getConfig } from '@edx/frontend-platform/config'; import Sidebar from './index'; import { SubsidyRequestsContext } from '../../components/subsidy-requests'; import { EnterpriseSubsidiesContext } from '../../components/EnterpriseSubsidiesContext'; import { EnterpriseAppContext } from '../../components/EnterpriseApp/EnterpriseAppContextProvider'; - +import LmsApiService from '../../data/services/LmsApiService'; import { features } from '../../config'; import { @@ -34,6 +34,8 @@ jest.mock('@edx/frontend-platform/config', () => ({ })), })); +jest.mock('../../data/services/LmsApiService'); + const mockStore = configureMockStore([thunk]); const initialState = { sidebar: { @@ -46,6 +48,9 @@ const initialState = { enableSubscriptionManagementScreen: true, enableAnalyticsScreen: true, enableReportingConfigScreenLink: true, + enterpriseFeatures: { + enterpriseGroupsV1: false, + }, }, }; @@ -127,6 +132,9 @@ describe('', () => { }, portalConfiguration: { enableCodeManagementScreen: false, + enterpriseFeatures: { + enterpriseGroupsV1: false, + }, }, }); @@ -421,6 +429,35 @@ describe('', () => { } }); + it('hides highlights when we have groups with a subset of all learners', async () => { + getConfig.mockReturnValue({ FEATURE_CONTENT_HIGHLIGHTS: true }); + const store = mockStore({ + ...initialState, + portalConfiguration: { + enterpriseFeatures: { + enterpriseGroupsV1: true, + }, + }, + }); + + LmsApiService.fetchEnterpriseGroup.mockImplementation(() => Promise.resolve({ + data: { results: { applies_to_all_contexts: false } }, + })); + render(); + const highlightsLink = expect(screen.queryByRole('link', { name: 'Highlights' })); + // we have to wait for the async call to set the state + setTimeout(() => { + expect(highlightsLink).not.toBeInTheDocument(); + }, 1000); + + LmsApiService.fetchEnterpriseGroup.mockImplementation(() => Promise.resolve({ + data: { results: { applies_to_all_contexts: true } }, + })); + render(); + setTimeout(() => { + expect(highlightsLink).toBeInTheDocument(); + }, 1000); + }); describe('notifications', () => { it('displays notification bubble when there are outstanding license requests', () => { const contextValue = { subsidyRequestsCounts: { subscriptionLicenses: 2 } }; diff --git a/src/containers/Sidebar/index.jsx b/src/containers/Sidebar/index.jsx index 27b36abaa4..c0cfa096f6 100644 --- a/src/containers/Sidebar/index.jsx +++ b/src/containers/Sidebar/index.jsx @@ -17,6 +17,7 @@ const mapStateToProps = state => ({ enableLearnerPortal: state.portalConfiguration.enableLearnerPortal, enableLmsConfigurationsScreen: state.portalConfiguration.enableLmsConfigurationsScreen, enableAnalyticsScreen: state.portalConfiguration.enableAnalyticsScreen, + enterpriseGroupsV1: state.portalConfiguration.enterpriseFeatures?.enterpriseGroupsV1, }); const mapDispatchToProps = dispatch => ({ diff --git a/src/data/services/LmsApiService.js b/src/data/services/LmsApiService.js index 686164ec4e..ae1059c918 100644 --- a/src/data/services/LmsApiService.js +++ b/src/data/services/LmsApiService.js @@ -420,6 +420,11 @@ class LmsApiService { return response; }; + static fetchEnterpriseGroup = async () => { + const url = `${LmsApiService.enterpriseGroupUrl}`; + return LmsApiService.apiClient().get(url); + }; + static inviteEnterpriseLearnersToGroup = async (groupUuid, formData) => { const assignLearnerEndpoint = `${LmsApiService.enterpriseGroupUrl}${groupUuid}/assign_learners/`; return LmsApiService.apiClient().post(assignLearnerEndpoint, formData); From 0a60778b0c9f39ac9abdd1d982a4dca7c0fed2be Mon Sep 17 00:00:00 2001 From: Kira Miller Date: Tue, 2 Apr 2024 21:40:53 +0000 Subject: [PATCH 2/2] fix: PR requests --- src/containers/Sidebar/Sidebar.test.jsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/containers/Sidebar/Sidebar.test.jsx b/src/containers/Sidebar/Sidebar.test.jsx index fff6d2471e..f5334ab4c3 100644 --- a/src/containers/Sidebar/Sidebar.test.jsx +++ b/src/containers/Sidebar/Sidebar.test.jsx @@ -441,7 +441,7 @@ describe('', () => { }); LmsApiService.fetchEnterpriseGroup.mockImplementation(() => Promise.resolve({ - data: { results: { applies_to_all_contexts: false } }, + data: { results: [{ applies_to_all_contexts: false }] }, })); render(); const highlightsLink = expect(screen.queryByRole('link', { name: 'Highlights' })); @@ -451,7 +451,7 @@ describe('', () => { }, 1000); LmsApiService.fetchEnterpriseGroup.mockImplementation(() => Promise.resolve({ - data: { results: { applies_to_all_contexts: true } }, + data: { results: [{ applies_to_all_contexts: true }] }, })); render(); setTimeout(() => {