From 45c68d6ca40cf2250f6b8b10fa58afb435561114 Mon Sep 17 00:00:00 2001 From: Demid Date: Thu, 12 Sep 2024 21:55:01 +0300 Subject: [PATCH] Hide "Advanced Settings" settings item [BB-9081] (#1252) * refactor: convert header utils to hooks * feat: hide advanced settings button if a user doesn't have access --- src/CourseAuthoringPage.jsx | 5 ++ src/header/Header.tsx | 13 ++-- src/header/{utils.js => hooks.js} | 70 +++++++++++++-------- src/header/{utils.test.js => hooks.test.js} | 43 +++++++++---- 4 files changed, 88 insertions(+), 43 deletions(-) rename src/header/{utils.js => hooks.js} (55%) rename src/header/{utils.test.js => hooks.test.js} (50%) diff --git a/src/CourseAuthoringPage.jsx b/src/CourseAuthoringPage.jsx index 5d3d5e37b3..41da0bc232 100644 --- a/src/CourseAuthoringPage.jsx +++ b/src/CourseAuthoringPage.jsx @@ -11,6 +11,7 @@ import { fetchCourseDetail } from './data/thunks'; import { useModel } from './generic/model-store'; import NotFoundAlert from './generic/NotFoundAlert'; import PermissionDeniedAlert from './generic/PermissionDeniedAlert'; +import { fetchStudioHomeData } from './studio-home/data/thunks'; import { getCourseAppsApiStatus } from './pages-and-resources/data/selectors'; import { RequestStatus } from './data/constants'; import Loading from './generic/Loading'; @@ -22,6 +23,10 @@ const CourseAuthoringPage = ({ courseId, children }) => { dispatch(fetchCourseDetail(courseId)); }, [courseId]); + useEffect(() => { + dispatch(fetchStudioHomeData()); + }, []); + const courseDetail = useModel('courseDetails', courseId); const courseNumber = courseDetail ? courseDetail.number : null; diff --git a/src/header/Header.tsx b/src/header/Header.tsx index ca109018f0..6bb916c397 100644 --- a/src/header/Header.tsx +++ b/src/header/Header.tsx @@ -6,7 +6,7 @@ import { useToggle } from '@openedx/paragon'; import { generatePath, useHref } from 'react-router-dom'; import { SearchModal } from '../search-modal'; -import { getContentMenuItems, getSettingMenuItems, getToolsMenuItems } from './utils'; +import { useContentMenuItems, useSettingMenuItems, useToolsMenuItems } from './hooks'; import messages from './messages'; interface HeaderProps { @@ -33,23 +33,28 @@ const Header = ({ const studioBaseUrl = getConfig().STUDIO_BASE_URL; const meiliSearchEnabled = [true, 'true'].includes(getConfig().MEILISEARCH_ENABLED); + + const contentMenuItems = useContentMenuItems(contextId); + const settingMenuItems = useSettingMenuItems(contextId); + const toolsMenuItems = useToolsMenuItems(contextId); const mainMenuDropdowns = !isLibrary ? [ { id: `${intl.formatMessage(messages['header.links.content'])}-dropdown-menu`, buttonTitle: intl.formatMessage(messages['header.links.content']), - items: getContentMenuItems({ studioBaseUrl, courseId: contextId, intl }), + items: contentMenuItems, }, { id: `${intl.formatMessage(messages['header.links.settings'])}-dropdown-menu`, buttonTitle: intl.formatMessage(messages['header.links.settings']), - items: getSettingMenuItems({ studioBaseUrl, courseId: contextId, intl }), + items: settingMenuItems, }, { id: `${intl.formatMessage(messages['header.links.tools'])}-dropdown-menu`, buttonTitle: intl.formatMessage(messages['header.links.tools']), - items: getToolsMenuItems({ studioBaseUrl, courseId: contextId, intl }), + items: toolsMenuItems, }, ] : []; + const outlineLink = !isLibrary ? `${studioBaseUrl}/course/${contextId}` : generatePath(libraryHref, { libraryId: contextId }); diff --git a/src/header/utils.js b/src/header/hooks.js similarity index 55% rename from src/header/utils.js rename to src/header/hooks.js index c8c7992b91..9bcbd29e4e 100644 --- a/src/header/utils.js +++ b/src/header/hooks.js @@ -1,8 +1,14 @@ import { getConfig } from '@edx/frontend-platform'; +import { useIntl } from '@edx/frontend-platform/i18n'; +import { useSelector } from 'react-redux'; import { getPagePath } from '../utils'; +import { getStudioHomeData } from '../studio-home/data/selectors'; import messages from './messages'; -export const getContentMenuItems = ({ studioBaseUrl, courseId, intl }) => { +export const useContentMenuItems = courseId => { + const intl = useIntl(); + const studioBaseUrl = getConfig().STUDIO_BASE_URL; + const items = [ { href: `${studioBaseUrl}/course/${courseId}`, @@ -31,7 +37,11 @@ export const getContentMenuItems = ({ studioBaseUrl, courseId, intl }) => { return items; }; -export const getSettingMenuItems = ({ studioBaseUrl, courseId, intl }) => { +export const useSettingMenuItems = courseId => { + const intl = useIntl(); + const studioBaseUrl = getConfig().STUDIO_BASE_URL; + const { canAccessAdvancedSettings } = useSelector(getStudioHomeData); + const items = [ { href: `${studioBaseUrl}/settings/details/${courseId}`, @@ -49,10 +59,12 @@ export const getSettingMenuItems = ({ studioBaseUrl, courseId, intl }) => { href: `${studioBaseUrl}/group_configurations/${courseId}`, title: intl.formatMessage(messages['header.links.groupConfigurations']), }, - { - href: `${studioBaseUrl}/settings/advanced/${courseId}`, - title: intl.formatMessage(messages['header.links.advancedSettings']), - }, + ...(canAccessAdvancedSettings === true + ? [{ + href: `${studioBaseUrl}/settings/advanced/${courseId}`, + title: intl.formatMessage(messages['header.links.advancedSettings']), + }] : [] + ), ]; if (getConfig().ENABLE_CERTIFICATE_PAGE === 'true') { items.push({ @@ -63,23 +75,29 @@ export const getSettingMenuItems = ({ studioBaseUrl, courseId, intl }) => { return items; }; -export const getToolsMenuItems = ({ studioBaseUrl, courseId, intl }) => ([ - { - href: `${studioBaseUrl}/import/${courseId}`, - title: intl.formatMessage(messages['header.links.import']), - }, - { - href: `${studioBaseUrl}/export/${courseId}`, - title: intl.formatMessage(messages['header.links.exportCourse']), - }, - ...(getConfig().ENABLE_TAGGING_TAXONOMY_PAGES === 'true' - ? [{ - href: '#export-tags', - title: intl.formatMessage(messages['header.links.exportTags']), - }] : [] - ), - { - href: `${studioBaseUrl}/checklists/${courseId}`, - title: intl.formatMessage(messages['header.links.checklists']), - }, -]); +export const useToolsMenuItems = courseId => { + const intl = useIntl(); + const studioBaseUrl = getConfig().STUDIO_BASE_URL; + + const items = [ + { + href: `${studioBaseUrl}/import/${courseId}`, + title: intl.formatMessage(messages['header.links.import']), + }, + { + href: `${studioBaseUrl}/export/${courseId}`, + title: intl.formatMessage(messages['header.links.exportCourse']), + }, + ...(getConfig().ENABLE_TAGGING_TAXONOMY_PAGES === 'true' + ? [{ + href: '#export-tags', + title: intl.formatMessage(messages['header.links.exportTags']), + }] : [] + ), + { + href: `${studioBaseUrl}/checklists/${courseId}`, + title: intl.formatMessage(messages['header.links.checklists']), + }, + ]; + return items; +}; diff --git a/src/header/utils.test.js b/src/header/hooks.test.js similarity index 50% rename from src/header/utils.test.js rename to src/header/hooks.test.js index f2c2f3acb5..9b9f1cbae2 100644 --- a/src/header/utils.test.js +++ b/src/header/hooks.test.js @@ -1,13 +1,19 @@ +import { useSelector } from 'react-redux'; import { getConfig, setConfig } from '@edx/frontend-platform'; -import { getContentMenuItems, getToolsMenuItems, getSettingMenuItems } from './utils'; +import { renderHook } from '@testing-library/react-hooks'; +import { useContentMenuItems, useToolsMenuItems, useSettingMenuItems } from './hooks'; -const props = { - studioBaseUrl: 'UrLSTuiO', - courseId: '123', - intl: { +jest.mock('@edx/frontend-platform/i18n', () => ({ + ...jest.requireActual('@edx/frontend-platform/i18n'), + useIntl: () => ({ formatMessage: jest.fn(message => message.defaultMessage), - }, -}; + }), +})); + +jest.mock('react-redux', () => ({ + ...jest.requireActual('react-redux'), + useSelector: jest.fn(), +})); describe('header utils', () => { describe('getContentMenuItems', () => { @@ -16,7 +22,7 @@ describe('header utils', () => { ...getConfig(), ENABLE_VIDEO_UPLOAD_PAGE_LINK_IN_CONTENT_DROPDOWN: 'true', }); - const actualItems = getContentMenuItems(props); + const actualItems = renderHook(() => useContentMenuItems('course-123')).result.current; expect(actualItems).toHaveLength(5); }); it('should not include Video Uploads option', () => { @@ -24,18 +30,20 @@ describe('header utils', () => { ...getConfig(), ENABLE_VIDEO_UPLOAD_PAGE_LINK_IN_CONTENT_DROPDOWN: 'false', }); - const actualItems = getContentMenuItems(props); + const actualItems = renderHook(() => useContentMenuItems('course-123')).result.current; expect(actualItems).toHaveLength(4); }); }); describe('getSettingsMenuitems', () => { + useSelector.mockReturnValue({ canAccessAdvancedSettings: true }); + it('should include certificates option', () => { setConfig({ ...getConfig(), ENABLE_CERTIFICATE_PAGE: 'true', }); - const actualItems = getSettingMenuItems(props); + const actualItems = renderHook(() => useSettingMenuItems('course-123')).result.current; expect(actualItems).toHaveLength(6); }); it('should not include certificates option', () => { @@ -43,9 +51,18 @@ describe('header utils', () => { ...getConfig(), ENABLE_CERTIFICATE_PAGE: 'false', }); - const actualItems = getSettingMenuItems(props); + const actualItems = renderHook(() => useSettingMenuItems('course-123')).result.current; expect(actualItems).toHaveLength(5); }); + it('should include advanced settings option', () => { + const actualItemsTitle = renderHook(() => useSettingMenuItems('course-123')).result.current.map((item) => item.title); + expect(actualItemsTitle).toContain('Advanced Settings'); + }); + it('should not include advanced settings option', () => { + useSelector.mockReturnValue({ canAccessAdvancedSettings: false }); + const actualItemsTitle = renderHook(() => useSettingMenuItems('course-123')).result.current.map((item) => item.title); + expect(actualItemsTitle).not.toContain('Advanced Settings'); + }); }); describe('getToolsMenuItems', () => { @@ -54,7 +71,7 @@ describe('header utils', () => { ...getConfig(), ENABLE_TAGGING_TAXONOMY_PAGES: 'true', }); - const actualItemsTitle = getToolsMenuItems(props).map((item) => item.title); + const actualItemsTitle = renderHook(() => useToolsMenuItems('course-123')).result.current.map((item) => item.title); expect(actualItemsTitle).toEqual([ 'Import', 'Export Course', @@ -67,7 +84,7 @@ describe('header utils', () => { ...getConfig(), ENABLE_TAGGING_TAXONOMY_PAGES: 'false', }); - const actualItemsTitle = getToolsMenuItems(props).map((item) => item.title); + const actualItemsTitle = renderHook(() => useToolsMenuItems('course-123')).result.current.map((item) => item.title); expect(actualItemsTitle).toEqual([ 'Import', 'Export Course',