feat: nav dropdowns in library authoring view (#2556)
Updates navbar in library authoring page to include `Team Access` and `Import` menu options. Clicking on `Team Access` button opens Team management modal. As per this new PR: https://github.com/openedx/frontend-app-authoring/pull/2570, if admin console url is set, it should be used instead of team access modal. So updated this PR accordingly.
This commit is contained in:
@@ -6,7 +6,7 @@ import { type Container, useToggle } from '@openedx/paragon';
|
||||
import { useWaffleFlags } from '../data/apiHooks';
|
||||
import { SearchModal } from '../search-modal';
|
||||
import {
|
||||
useContentMenuItems, useLibraryToolsMenuItems, useSettingMenuItems, useToolsMenuItems,
|
||||
useContentMenuItems, useLibrarySettingsMenuItems, useLibraryToolsMenuItems, useSettingMenuItems, useToolsMenuItems,
|
||||
} from './hooks';
|
||||
import messages from './messages';
|
||||
|
||||
@@ -20,6 +20,7 @@ interface HeaderProps {
|
||||
isHiddenMainMenu?: boolean,
|
||||
isLibrary?: boolean,
|
||||
containerProps?: ContainerPropsType,
|
||||
readOnly?: boolean,
|
||||
}
|
||||
|
||||
const Header = ({
|
||||
@@ -30,6 +31,7 @@ const Header = ({
|
||||
isHiddenMainMenu = false,
|
||||
isLibrary = false,
|
||||
containerProps = {},
|
||||
readOnly = false,
|
||||
}: HeaderProps) => {
|
||||
const intl = useIntl();
|
||||
const waffleFlags = useWaffleFlags();
|
||||
@@ -43,7 +45,8 @@ const Header = ({
|
||||
const settingMenuItems = useSettingMenuItems(contextId);
|
||||
const toolsMenuItems = useToolsMenuItems(contextId);
|
||||
const libraryToolsMenuItems = useLibraryToolsMenuItems(contextId);
|
||||
const mainMenuDropdowns = !isLibrary ? [
|
||||
const libraryToolsSettingsItems = useLibrarySettingsMenuItems(contextId, readOnly);
|
||||
let mainMenuDropdowns = !isLibrary ? [
|
||||
{
|
||||
id: `${intl.formatMessage(messages['header.links.content'])}-dropdown-menu`,
|
||||
buttonTitle: intl.formatMessage(messages['header.links.content']),
|
||||
@@ -65,6 +68,18 @@ const Header = ({
|
||||
items: libraryToolsMenuItems,
|
||||
}];
|
||||
|
||||
// Include settings menu only if user is allowed to see them.
|
||||
if (isLibrary && libraryToolsSettingsItems.length > 0) {
|
||||
mainMenuDropdowns = [
|
||||
{
|
||||
id: `${intl.formatMessage(messages['header.links.settings'])}-dropdown-menu`,
|
||||
buttonTitle: intl.formatMessage(messages['header.links.settings']),
|
||||
items: libraryToolsSettingsItems,
|
||||
},
|
||||
...mainMenuDropdowns,
|
||||
];
|
||||
}
|
||||
|
||||
const getOutlineLink = () => {
|
||||
if (isLibrary) {
|
||||
return `/library/${contextId}`;
|
||||
|
||||
@@ -2,7 +2,9 @@ import { useSelector } from 'react-redux';
|
||||
import { getConfig, setConfig } from '@edx/frontend-platform';
|
||||
import { renderHook } from '@testing-library/react';
|
||||
import messages from './messages';
|
||||
import { useContentMenuItems, useToolsMenuItems, useSettingMenuItems } from './hooks';
|
||||
import {
|
||||
useContentMenuItems, useToolsMenuItems, useSettingMenuItems, useLibrarySettingsMenuItems, useLibraryToolsMenuItems,
|
||||
} from './hooks';
|
||||
import { mockWaffleFlags } from '../data/apiHooks.mock';
|
||||
|
||||
jest.mock('@edx/frontend-platform/i18n', () => ({
|
||||
@@ -28,7 +30,7 @@ jest.mock('react-redux', () => ({
|
||||
describe('header utils', () => {
|
||||
describe('getContentMenuItems', () => {
|
||||
it('when video upload page enabled should include Video Uploads option', () => {
|
||||
useSelector.mockReturnValue({
|
||||
jest.mocked(useSelector).mockReturnValue({
|
||||
librariesV2Enabled: false,
|
||||
});
|
||||
setConfig({
|
||||
@@ -39,7 +41,7 @@ describe('header utils', () => {
|
||||
expect(actualItems).toHaveLength(5);
|
||||
});
|
||||
it('when video upload page disabled should not include Video Uploads option', () => {
|
||||
useSelector.mockReturnValue({
|
||||
jest.mocked(useSelector).mockReturnValue({
|
||||
librariesV2Enabled: false,
|
||||
});
|
||||
setConfig({
|
||||
@@ -50,7 +52,7 @@ describe('header utils', () => {
|
||||
expect(actualItems).toHaveLength(4);
|
||||
});
|
||||
it('adds course libraries link to content menu when libraries v2 is enabled', () => {
|
||||
useSelector.mockReturnValue({
|
||||
jest.mocked(useSelector).mockReturnValue({
|
||||
librariesV2Enabled: true,
|
||||
});
|
||||
const actualItems = renderHook(() => useContentMenuItems('course-123')).result.current;
|
||||
@@ -60,7 +62,7 @@ describe('header utils', () => {
|
||||
|
||||
describe('getSettingsMenuitems', () => {
|
||||
beforeEach(() => {
|
||||
useSelector.mockReturnValue({
|
||||
jest.mocked(useSelector).mockReturnValue({
|
||||
canAccessAdvancedSettings: true,
|
||||
});
|
||||
});
|
||||
@@ -86,7 +88,7 @@ describe('header utils', () => {
|
||||
expect(actualItemsTitle).toContain('Advanced Settings');
|
||||
});
|
||||
it('when user has no access to advanced settings should not include advanced settings option', () => {
|
||||
useSelector.mockReturnValue({ canAccessAdvancedSettings: false });
|
||||
jest.mocked(useSelector).mockReturnValue({ canAccessAdvancedSettings: false });
|
||||
const actualItemsTitle = renderHook(() => useSettingMenuItems('course-123')).result.current.map((item) => item.title);
|
||||
expect(actualItemsTitle).not.toContain('Advanced Settings');
|
||||
});
|
||||
@@ -137,4 +139,44 @@ describe('header utils', () => {
|
||||
expect(actualItemsTitle).not.toContain(messages['header.links.optimizer'].defaultMessage);
|
||||
});
|
||||
});
|
||||
|
||||
describe('useLibrarySettingsMenuItems', () => {
|
||||
it('should contain team access url', () => {
|
||||
const items = renderHook(() => useLibrarySettingsMenuItems('library-123', false)).result.current;
|
||||
expect(items).toContainEqual({ title: 'Team Access', href: 'http://localhost/?sa=manage-team' });
|
||||
});
|
||||
it('should contain admin console url if set', () => {
|
||||
setConfig({
|
||||
...getConfig(),
|
||||
ADMIN_CONSOLE_URL: 'http://admin-console.com',
|
||||
});
|
||||
const items = renderHook(() => useLibrarySettingsMenuItems('library-123', false)).result.current;
|
||||
expect(items).toContainEqual({
|
||||
title: 'Team Access',
|
||||
href: 'http://admin-console.com/authz/libraries/library-123',
|
||||
});
|
||||
});
|
||||
it('should contain admin console url if set and readOnly is true', () => {
|
||||
setConfig({
|
||||
...getConfig(),
|
||||
ADMIN_CONSOLE_URL: 'http://admin-console.com',
|
||||
});
|
||||
const items = renderHook(() => useLibrarySettingsMenuItems('library-123', true)).result.current;
|
||||
expect(items).toContainEqual({
|
||||
title: 'Team Access',
|
||||
href: 'http://admin-console.com/authz/libraries/library-123',
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('useLibraryToolsMenuItems', () => {
|
||||
it('should contain backup and import url', () => {
|
||||
const items = renderHook(() => useLibraryToolsMenuItems('course-123')).result.current;
|
||||
expect(items).toContainEqual({
|
||||
href: '/library/course-123/backup',
|
||||
title: 'Backup to local archive',
|
||||
});
|
||||
expect(items).toContainEqual({ href: '/library/course-123/import', title: 'Import' });
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -3,13 +3,15 @@ import { useIntl } from '@edx/frontend-platform/i18n';
|
||||
import { useSelector } from 'react-redux';
|
||||
import { Badge } from '@openedx/paragon';
|
||||
|
||||
import { getPagePath } from '../utils';
|
||||
import { useWaffleFlags } from '../data/apiHooks';
|
||||
import { getStudioHomeData } from '../studio-home/data/selectors';
|
||||
import { getPagePath } from '@src/utils';
|
||||
import { useWaffleFlags } from '@src/data/apiHooks';
|
||||
import { getStudioHomeData } from '@src/studio-home/data/selectors';
|
||||
import courseOptimizerMessages from '@src/optimizer-page/messages';
|
||||
import { SidebarActions } from '@src/library-authoring/common/context/SidebarContext';
|
||||
import { LibQueryParamKeys } from '@src/library-authoring/routes';
|
||||
import messages from './messages';
|
||||
import courseOptimizerMessages from '../optimizer-page/messages';
|
||||
|
||||
export const useContentMenuItems = courseId => {
|
||||
export const useContentMenuItems = (courseId: string) => {
|
||||
const intl = useIntl();
|
||||
const studioBaseUrl = getConfig().STUDIO_BASE_URL;
|
||||
const waffleFlags = useWaffleFlags();
|
||||
@@ -50,7 +52,7 @@ export const useContentMenuItems = courseId => {
|
||||
return items;
|
||||
};
|
||||
|
||||
export const useSettingMenuItems = courseId => {
|
||||
export const useSettingMenuItems = (courseId: string) => {
|
||||
const intl = useIntl();
|
||||
const studioBaseUrl = getConfig().STUDIO_BASE_URL;
|
||||
const { canAccessAdvancedSettings } = useSelector(getStudioHomeData);
|
||||
@@ -89,7 +91,7 @@ export const useSettingMenuItems = courseId => {
|
||||
return items;
|
||||
};
|
||||
|
||||
export const useToolsMenuItems = (courseId) => {
|
||||
export const useToolsMenuItems = (courseId: string) => {
|
||||
const intl = useIntl();
|
||||
const studioBaseUrl = getConfig().STUDIO_BASE_URL;
|
||||
const waffleFlags = useWaffleFlags();
|
||||
@@ -127,7 +129,7 @@ export const useToolsMenuItems = (courseId) => {
|
||||
return items;
|
||||
};
|
||||
|
||||
export const useLibraryToolsMenuItems = itemId => {
|
||||
export const useLibraryToolsMenuItems = (itemId: string) => {
|
||||
const intl = useIntl();
|
||||
|
||||
const items = [
|
||||
@@ -135,7 +137,49 @@ export const useLibraryToolsMenuItems = itemId => {
|
||||
href: `/library/${itemId}/backup`,
|
||||
title: intl.formatMessage(messages['header.links.exportLibrary']),
|
||||
},
|
||||
{
|
||||
href: `/library/${itemId}/import`,
|
||||
title: intl.formatMessage(messages['header.links.lib.import']),
|
||||
},
|
||||
];
|
||||
|
||||
return items;
|
||||
};
|
||||
|
||||
export const useLibrarySettingsMenuItems = (itemId: string, readOnly: boolean) => {
|
||||
const intl = useIntl();
|
||||
|
||||
const openTeamAccessModalUrl = () => {
|
||||
const adminConsoleUrl = getConfig().ADMIN_CONSOLE_URL;
|
||||
// always show link to admin console MFE if it is being used
|
||||
const shouldShowAdminConsoleLink = !!adminConsoleUrl;
|
||||
|
||||
// if the admin console MFE isn't being used, show team modal button for non–read-only users
|
||||
const shouldShowTeamModalButton = !adminConsoleUrl && !readOnly;
|
||||
if (shouldShowTeamModalButton) {
|
||||
if (!window.location.href) {
|
||||
return null;
|
||||
}
|
||||
const url = new URL(window.location.href);
|
||||
// Set ?sa=manage-team in url which in turn opens team access modal
|
||||
url.searchParams.set(LibQueryParamKeys.SidebarActions, SidebarActions.ManageTeam);
|
||||
return url.toString();
|
||||
}
|
||||
if (shouldShowAdminConsoleLink) {
|
||||
return `${adminConsoleUrl}/authz/libraries/${itemId}`;
|
||||
}
|
||||
return null;
|
||||
};
|
||||
|
||||
const items: { title: string; href: string }[] = [];
|
||||
|
||||
const teamAccessUrl = openTeamAccessModalUrl();
|
||||
if (teamAccessUrl) {
|
||||
items.push({
|
||||
title: intl.formatMessage(messages['header.menu.teamAccess']),
|
||||
href: teamAccessUrl,
|
||||
});
|
||||
}
|
||||
|
||||
return items;
|
||||
};
|
||||
@@ -96,6 +96,11 @@ const messages = defineMessages({
|
||||
defaultMessage: 'Import',
|
||||
description: 'Link to Studio Import page',
|
||||
},
|
||||
'header.links.lib.import': {
|
||||
id: 'header.links.lib.import',
|
||||
defaultMessage: 'Import',
|
||||
description: 'Link to Course Import page in library',
|
||||
},
|
||||
'header.links.exportCourse': {
|
||||
id: 'header.links.exportCourse',
|
||||
defaultMessage: 'Export Course',
|
||||
@@ -106,6 +111,11 @@ const messages = defineMessages({
|
||||
defaultMessage: 'Backup to local archive',
|
||||
description: 'Link to Studio Backup Library page',
|
||||
},
|
||||
'header.menu.teamAccess': {
|
||||
id: 'header.links.teamAccess',
|
||||
defaultMessage: 'Team Access',
|
||||
description: 'Menu item to open team access popup',
|
||||
},
|
||||
'header.links.optimizer': {
|
||||
id: 'header.links.optimizer',
|
||||
defaultMessage: 'Course Optimizer',
|
||||
@@ -18,6 +18,7 @@ import { CreateContainerModal } from './create-container';
|
||||
import { ROUTES } from './routes';
|
||||
import { LibrarySectionPage, LibrarySubsectionPage } from './section-subsections';
|
||||
import { LibraryUnitPage } from './units';
|
||||
import { LibraryTeamModal } from './library-team';
|
||||
|
||||
const LibraryLayoutWrapper: React.FC<React.PropsWithChildren> = ({ children }) => {
|
||||
const {
|
||||
@@ -48,6 +49,7 @@ const LibraryLayoutWrapper: React.FC<React.PropsWithChildren> = ({ children }) =
|
||||
<CreateCollectionModal />
|
||||
<CreateContainerModal />
|
||||
<ComponentEditorModal />
|
||||
<LibraryTeamModal />
|
||||
</SidebarProvider>
|
||||
</LibraryProvider>
|
||||
);
|
||||
|
||||
@@ -24,7 +24,7 @@ import { useContentLibrary } from '@src/library-authoring/data/apiHooks';
|
||||
|
||||
export const LibraryBackupPage = () => {
|
||||
const intl = useIntl();
|
||||
const { libraryId } = useLibraryContext();
|
||||
const { libraryId, readOnly } = useLibraryContext();
|
||||
const [taskId, setTaskId] = useState<string>('');
|
||||
const [isMutationInProgress, setIsMutationInProgress] = useState<boolean>(false);
|
||||
const timeoutRef = useRef<NodeJS.Timeout | null>(null);
|
||||
@@ -144,6 +144,7 @@ export const LibraryBackupPage = () => {
|
||||
title={libraryData.title}
|
||||
org={libraryData.org}
|
||||
contextId={libraryId}
|
||||
readOnly={readOnly}
|
||||
isLibrary
|
||||
containerProps={{
|
||||
size: undefined,
|
||||
|
||||
@@ -107,6 +107,7 @@ const LibraryCollectionPage = () => {
|
||||
showOnlyPublished,
|
||||
extraFilter: contextExtraFilter,
|
||||
setCollectionId,
|
||||
readOnly,
|
||||
} = useLibraryContext();
|
||||
const { sidebarItemInfo } = useSidebarContext();
|
||||
|
||||
@@ -194,6 +195,7 @@ const LibraryCollectionPage = () => {
|
||||
title={libraryData.title}
|
||||
org={libraryData.org}
|
||||
contextId={libraryId}
|
||||
readOnly={readOnly}
|
||||
isLibrary
|
||||
containerProps={{
|
||||
size: undefined,
|
||||
|
||||
@@ -7,10 +7,10 @@ import {
|
||||
useState,
|
||||
} from 'react';
|
||||
import { useParams } from 'react-router-dom';
|
||||
import { useStateWithUrlSearchParam } from '../../../hooks';
|
||||
import { useStateWithUrlSearchParam } from '@src/hooks';
|
||||
import { LibQueryParamKeys, useLibraryRoutes } from '@src/library-authoring/routes';
|
||||
import { useComponentPickerContext } from './ComponentPickerContext';
|
||||
import { useLibraryContext } from './LibraryContext';
|
||||
import { useLibraryRoutes } from '../../routes';
|
||||
|
||||
export enum SidebarBodyItemId {
|
||||
AddContent = 'add-content',
|
||||
@@ -130,14 +130,14 @@ export const SidebarProvider = ({
|
||||
|
||||
const [sidebarTab, setSidebarTab] = useStateWithUrlSearchParam<SidebarInfoTab>(
|
||||
defaultTab.component,
|
||||
'st',
|
||||
LibQueryParamKeys.SidebarTab,
|
||||
(value: string) => toSidebarInfoTab(value),
|
||||
(value: SidebarInfoTab) => value.toString(),
|
||||
);
|
||||
|
||||
const [sidebarAction, setSidebarAction] = useStateWithUrlSearchParam<SidebarActions>(
|
||||
SidebarActions.None,
|
||||
'sa',
|
||||
LibQueryParamKeys.SidebarActions,
|
||||
(value: string) => Object.values(SidebarActions).find((enumValue) => value === enumValue),
|
||||
(value: SidebarActions) => value.toString(),
|
||||
);
|
||||
|
||||
@@ -5,15 +5,13 @@ import { FormattedDate, useIntl } from '@edx/frontend-platform/i18n';
|
||||
|
||||
import messages from './messages';
|
||||
import LibraryPublishStatus from './LibraryPublishStatus';
|
||||
import { LibraryTeamModal } from '../library-team';
|
||||
import { useLibraryContext } from '../common/context/LibraryContext';
|
||||
import { SidebarActions, useSidebarContext } from '../common/context/SidebarContext';
|
||||
|
||||
const LibraryInfo = () => {
|
||||
const intl = useIntl();
|
||||
const { libraryId, libraryData, readOnly } = useLibraryContext();
|
||||
const { sidebarAction, setSidebarAction, resetSidebarAction } = useSidebarContext();
|
||||
const isLibraryTeamModalOpen = (sidebarAction === SidebarActions.ManageTeam);
|
||||
const { setSidebarAction } = useSidebarContext();
|
||||
const adminConsoleUrl = getConfig().ADMIN_CONSOLE_URL;
|
||||
|
||||
// always show link to admin console MFE if it is being used
|
||||
@@ -25,9 +23,6 @@ const LibraryInfo = () => {
|
||||
const openLibraryTeamModal = useCallback(() => {
|
||||
setSidebarAction(SidebarActions.ManageTeam);
|
||||
}, [setSidebarAction]);
|
||||
const closeLibraryTeamModal = useCallback(() => {
|
||||
resetSidebarAction();
|
||||
}, [resetSidebarAction]);
|
||||
|
||||
return (
|
||||
<Stack direction="vertical" gap={2.5}>
|
||||
@@ -81,7 +76,6 @@ const LibraryInfo = () => {
|
||||
</span>
|
||||
</Stack>
|
||||
</Stack>
|
||||
{isLibraryTeamModalOpen && <LibraryTeamModal onClose={closeLibraryTeamModal} />}
|
||||
</Stack>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -1,24 +1,24 @@
|
||||
import React from 'react';
|
||||
|
||||
import { StandardModal } from '@openedx/paragon';
|
||||
import { useIntl } from '@edx/frontend-platform/i18n';
|
||||
|
||||
import { useCallback } from 'react';
|
||||
import { SidebarActions, useSidebarContext } from '@src/library-authoring/common/context/SidebarContext';
|
||||
import LibraryTeam from './LibraryTeam';
|
||||
import messages from './messages';
|
||||
|
||||
interface LibraryTeamModalProps {
|
||||
onClose: () => void;
|
||||
}
|
||||
|
||||
export const LibraryTeamModal: React.FC<LibraryTeamModalProps> = ({
|
||||
onClose,
|
||||
}) => {
|
||||
export const LibraryTeamModal = () => {
|
||||
const intl = useIntl();
|
||||
const { sidebarAction, resetSidebarAction } = useSidebarContext();
|
||||
// Open the library team modal only when Manage Team sidebar action is set
|
||||
const isOpen = (sidebarAction === SidebarActions.ManageTeam);
|
||||
const onClose = useCallback(() => {
|
||||
resetSidebarAction();
|
||||
}, [resetSidebarAction]);
|
||||
|
||||
// Show Library Team modal in full screen
|
||||
return (
|
||||
<StandardModal
|
||||
isOpen
|
||||
isOpen={isOpen}
|
||||
title={intl.formatMessage(messages.modalTitle)}
|
||||
onClose={onClose}
|
||||
size="lg"
|
||||
|
||||
@@ -14,6 +14,11 @@ import { ContainerType, getBlockType } from '../generic/key-utils';
|
||||
|
||||
export const BASE_ROUTE = '/library/:libraryId';
|
||||
|
||||
export enum LibQueryParamKeys {
|
||||
SidebarActions = 'sa',
|
||||
SidebarTab = 'st',
|
||||
}
|
||||
|
||||
export const ROUTES = {
|
||||
// LibraryAuthoringPage routes:
|
||||
// * Components tab, with an optionally selected component in the sidebar.
|
||||
@@ -240,7 +245,7 @@ export const useLibraryRoutes = (): LibraryRoutesData => {
|
||||
}
|
||||
|
||||
// Also remove the `sa` (sidebar action) search param if it exists.
|
||||
searchParams.delete('sa');
|
||||
searchParams.delete(LibQueryParamKeys.SidebarActions);
|
||||
|
||||
const newPath = generatePath(BASE_ROUTE + route, routeParams);
|
||||
// Prevent unnecessary navigation if the path is the same.
|
||||
|
||||
@@ -20,7 +20,7 @@ import { ContainerEditableTitle, FooterActions, HeaderActions } from '../contain
|
||||
/** Full library section page */
|
||||
export const LibrarySectionPage = () => {
|
||||
const intl = useIntl();
|
||||
const { libraryId, containerId } = useLibraryContext();
|
||||
const { libraryId, containerId, readOnly } = useLibraryContext();
|
||||
const {
|
||||
sidebarItemInfo,
|
||||
} = useSidebarContext();
|
||||
@@ -84,6 +84,7 @@ export const LibrarySectionPage = () => {
|
||||
org={libraryData.org}
|
||||
contextId={libraryData.id}
|
||||
isLibrary
|
||||
readOnly={readOnly}
|
||||
containerProps={{
|
||||
size: undefined,
|
||||
}}
|
||||
|
||||
@@ -22,7 +22,7 @@ import { ContainerEditableTitle, FooterActions, HeaderActions } from '../contain
|
||||
/** Full library subsection page */
|
||||
export const LibrarySubsectionPage = () => {
|
||||
const intl = useIntl();
|
||||
const { libraryId, containerId } = useLibraryContext();
|
||||
const { libraryId, containerId, readOnly } = useLibraryContext();
|
||||
const { sidebarItemInfo } = useSidebarContext();
|
||||
|
||||
const { data: libraryData, isPending: isLibPending } = useContentLibrary(libraryId);
|
||||
@@ -64,6 +64,7 @@ export const LibrarySubsectionPage = () => {
|
||||
title={libraryData.title}
|
||||
org={libraryData.org}
|
||||
contextId={libraryData.id}
|
||||
readOnly={readOnly}
|
||||
isLibrary
|
||||
containerProps={{
|
||||
size: undefined,
|
||||
|
||||
@@ -23,10 +23,7 @@ import { ContainerEditableTitle, FooterActions, HeaderActions } from '../contain
|
||||
export const LibraryUnitPage = () => {
|
||||
const intl = useIntl();
|
||||
|
||||
const {
|
||||
libraryId,
|
||||
containerId,
|
||||
} = useLibraryContext();
|
||||
const { libraryId, containerId, readOnly } = useLibraryContext();
|
||||
|
||||
// istanbul ignore if: this should never happen
|
||||
if (!containerId) {
|
||||
@@ -71,6 +68,7 @@ export const LibraryUnitPage = () => {
|
||||
org={libraryData.org}
|
||||
contextId={libraryId}
|
||||
isLibrary
|
||||
readOnly={readOnly}
|
||||
containerProps={{
|
||||
size: undefined,
|
||||
}}
|
||||
|
||||
@@ -63,6 +63,7 @@ const slice = createSlice({
|
||||
studioShortName?: string;
|
||||
techSupportEmail?: string;
|
||||
userIsActive?: boolean;
|
||||
canAccessAdvancedSettings?: boolean;
|
||||
},
|
||||
studioHomeCoursesRequestParams: studioHomeCoursesRequestParamsDefault,
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user