fix: update sidebar design (#2852)

This commit is contained in:
Rômulo Penido
2026-02-05 17:44:46 -03:00
committed by GitHub
parent f900ace15c
commit 7173b71cc4
8 changed files with 80 additions and 97 deletions

View File

@@ -11,7 +11,7 @@ import VideoSelectorContainer from './selectors/VideoSelectorContainer';
import CustomPages from './custom-pages';
import { FilesPage, VideosPage } from './files-and-videos';
import { AdvancedSettings } from './advanced-settings';
import { CourseOutline } from './course-outline';
import { CourseOutline, OutlineSidebarPagesProvider } from './course-outline';
import ScheduleAndDetails from './schedule-and-details';
import { GradingSettings } from './grading-settings';
import CourseTeam from './course-team/CourseTeam';
@@ -58,7 +58,13 @@ const CourseAuthoringRoutes = () => {
<Routes>
<Route
path="/"
element={<PageWrap><CourseOutline /></PageWrap>}
element={(
<PageWrap>
<OutlineSidebarPagesProvider>
<CourseOutline />
</OutlineSidebarPagesProvider>
</PageWrap>
)}
/>
<Route
path="course_info"

View File

@@ -46,6 +46,7 @@ import {
} from './__mocks__';
import { COURSE_BLOCK_NAMES, VIDEO_SHARING_OPTIONS } from './constants';
import CourseOutline from './CourseOutline';
import { OutlineSidebarPagesProvider } from './outline-sidebar/OutlineSidebarPagesContext';
import messages from './messages';
import headerMessages from './header-navigations/messages';
@@ -139,7 +140,9 @@ const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
const renderComponent = () => render(
<CourseAuthoringProvider courseId={courseId}>
<CourseOutline />
<OutlineSidebarPagesProvider>
<CourseOutline />
</OutlineSidebarPagesProvider>
</CourseAuthoringProvider>,
);

View File

@@ -1 +1,2 @@
export { default as CourseOutline } from './CourseOutline';
export { OutlineSidebarPagesProvider } from './outline-sidebar/OutlineSidebarPagesContext';

View File

@@ -1,10 +1,12 @@
import { Hyperlink } from '@openedx/paragon';
import { useIntl } from '@edx/frontend-platform/i18n';
import { SchoolOutline } from '@openedx/paragon/icons';
import { HelpSidebar } from '@src/generic/help-sidebar';
import { SidebarContent, SidebarSection, SidebarTitle } from '@src/generic/sidebar';
import { useHelpUrls } from '@src/help-urls/hooks';
import { useCourseAuthoringContext } from '@src/CourseAuthoringContext';
import { useCourseDetails } from '../data/apiHooks';
import { getFormattedSidebarMessages } from './utils';
const OutlineHelpSideBar = () => {
@@ -15,6 +17,7 @@ const OutlineHelpSideBar = () => {
outline: learnMoreOutlineUrl,
} = useHelpUrls(['visibility', 'grading', 'outline']);
const { courseId } = useCourseAuthoringContext();
const { data: courseDetails } = useCourseDetails(courseId);
const sidebarMessages = getFormattedSidebarMessages(
{
@@ -26,24 +29,23 @@ const OutlineHelpSideBar = () => {
);
return (
<HelpSidebar
courseId={courseId}
showOtherSettings={false}
className="outline-sidebar mt-4"
data-testid="outline-sidebar"
>
{sidebarMessages.map(({ title, descriptions, link }, index) => {
const isLastSection = index === sidebarMessages.length - 1;
return (
<div className="outline-sidebar-section" key={title}>
<h4 className="help-sidebar-about-title">{title}</h4>
<>
<SidebarTitle
title={courseDetails?.title || ''}
icon={SchoolOutline}
/>
<SidebarContent>
{sidebarMessages.map(({ title, descriptions, link }) => (
<SidebarSection
key={title}
title={title}
>
{descriptions.map((description) => (
<p className="help-sidebar-about-descriptions" key={description}>{description}</p>
<p className="x-small" key={description}>{description}</p>
))}
{!!link?.href && (
<Hyperlink
className="small"
className="x-small"
destination={link.href}
target="_blank"
showLaunchIcon={false}
@@ -51,11 +53,10 @@ const OutlineHelpSideBar = () => {
{link.text}
</Hyperlink>
)}
{!isLastSection && <hr className="my-3.5" />}
</div>
);
})}
</HelpSidebar>
</SidebarSection>
))}
</SidebarContent>
</>
);
};

View File

@@ -7,6 +7,7 @@ import {
import { CourseAuthoringProvider } from '@src/CourseAuthoringContext';
import { OutlineSidebarProvider } from './OutlineSidebarContext';
import { OutlineSidebarPagesProvider } from './OutlineSidebarPagesContext';
import OutlineSidebar from './OutlineSidebar';
// Mock the useCourseDetails hook
@@ -19,9 +20,11 @@ const courseId = '123';
const extraWrapper = ({ children }) => (
<CourseAuthoringProvider courseId={courseId}>
<OutlineSidebarProvider>
{children}
</OutlineSidebarProvider>
<OutlineSidebarPagesProvider>
<OutlineSidebarProvider>
{children}
</OutlineSidebarProvider>
</OutlineSidebarPagesProvider>
</CourseAuthoringProvider>
);

View File

@@ -1,4 +1,4 @@
import { createContext, useContext } from 'react';
import { createContext, useContext, useMemo } from 'react';
import { getConfig } from '@edx/frontend-platform';
import {
HelpOutline, Info, Plus, Tag,
@@ -19,15 +19,18 @@ export type OutlineSidebarPages = {
align?: SidebarPage;
};
const showAlignSidebar = getConfig().ENABLE_TAGGING_TAXONOMY_PAGES === 'true';
const OUTLINE_SIDEBAR_PAGES: OutlineSidebarPages = {
const getOutlineSidebarPages = () => ({
info: {
component: OutlineInfoSidebar,
icon: Info,
title: messages.sidebarButtonInfo,
},
...(showAlignSidebar && {
add: {
component: AddSidebar,
icon: Plus,
title: messages.sidebarButtonAdd,
},
...(getConfig().ENABLE_TAGGING_TAXONOMY_PAGES === 'true' && {
align: {
component: OutlineAlignSidebar,
icon: Tag,
@@ -39,12 +42,7 @@ const OUTLINE_SIDEBAR_PAGES: OutlineSidebarPages = {
icon: HelpOutline,
title: messages.sidebarButtonHelp,
},
add: {
component: AddSidebar,
icon: Plus,
title: messages.sidebarButtonAdd,
},
};
});
/**
* Context for the Outline Sidebar Pages.
@@ -77,6 +75,24 @@ const OUTLINE_SIDEBAR_PAGES: OutlineSidebarPages = {
* );
*}
*/
export const OutlineSidebarPagesContext = createContext<OutlineSidebarPages>(OUTLINE_SIDEBAR_PAGES);
export const OutlineSidebarPagesContext = createContext<OutlineSidebarPages | undefined>(undefined);
export const useOutlineSidebarPagesContext = (): OutlineSidebarPages => useContext(OutlineSidebarPagesContext);
type OutlineSidebarPagesProviderProps = {
children: React.ReactNode;
};
export const OutlineSidebarPagesProvider = ({ children }: OutlineSidebarPagesProviderProps) => {
const sidebarPages = useMemo(getOutlineSidebarPages, []);
return (
<OutlineSidebarPagesContext.Provider value={sidebarPages}>
{children}
</OutlineSidebarPagesContext.Provider>
);
};
export const useOutlineSidebarPagesContext = (): OutlineSidebarPages => {
const ctx = useContext(OutlineSidebarPagesContext);
if (ctx === undefined) { throw new Error('useOutlineSidebarPages must be used within an OutlineSidebarPagesProvider'); }
return ctx;
};

View File

@@ -1,45 +0,0 @@
import { getConfig } from '@edx/frontend-platform';
import {
HelpOutline, Info, Plus, Tag,
} from '@openedx/paragon/icons';
import type { SidebarPage } from '@src/generic/sidebar';
import OutlineHelpSidebar from './OutlineHelpSidebar';
import { OutlineInfoSidebar } from './OutlineInfoSidebar';
import messages from './messages';
import { AddSidebar } from './AddSidebar';
import { OutlineAlignSidebar } from './OutlineAlignSidebar';
export type OutlineSidebarPages = {
info: SidebarPage;
help: SidebarPage;
add: SidebarPage;
align?: SidebarPage;
};
export const getOutlineSidebarPages = (): OutlineSidebarPages => {
const showAlignSidebar = getConfig().ENABLE_TAGGING_TAXONOMY_PAGES === 'true';
return {
info: {
component: OutlineInfoSidebar,
icon: Info,
title: messages.sidebarButtonInfo,
},
...(showAlignSidebar && {
align: {
component: OutlineAlignSidebar,
icon: Tag,
title: messages.sidebarButtonAlign,
},
}),
help: {
component: OutlineHelpSidebar,
icon: HelpOutline,
title: messages.sidebarButtonHelp,
},
add: {
component: AddSidebar,
icon: Plus,
title: messages.sidebarButtonAdd,
},
} satisfies OutlineSidebarPages;
};

View File

@@ -581,17 +581,16 @@ describe('<CourseUnit />', () => {
published_preview_link: publishedPreviewLink,
} = courseSectionVerticalMock;
await waitFor(async () => {
const viewLiveButton = screen.getByRole('button', { name: headerNavigationsMessages.viewLiveButton.defaultMessage });
await user.click(viewLiveButton);
expect(window.open).toHaveBeenCalled();
expect(window.open).toHaveBeenCalledWith(publishedPreviewLink, '_blank');
const viewLiveButton = await screen.findByRole('button', { name: headerNavigationsMessages.viewLiveButton.defaultMessage });
const previewButton = screen.getByRole('button', { name: headerNavigationsMessages.previewButton.defaultMessage });
await user.click(previewButton);
expect(window.open).toHaveBeenCalled();
expect(window.open).toHaveBeenCalledWith(draftPreviewLink, '_blank');
});
await user.click(viewLiveButton);
expect(window.open).toHaveBeenCalled();
expect(window.open).toHaveBeenCalledWith(publishedPreviewLink, '_blank');
const previewButton = screen.getByRole('button', { name: headerNavigationsMessages.previewButton.defaultMessage });
await user.click(previewButton);
expect(window.open).toHaveBeenCalled();
expect(window.open).toHaveBeenCalledWith(draftPreviewLink, '_blank');
window.open = open;
});
@@ -870,9 +869,8 @@ describe('<CourseUnit />', () => {
.reply(200, courseCreateXblockMock);
render(<RootWrapper />);
await waitFor(async () => {
await user.click(screen.getByRole('button', { name: legacySidebarMessages.actionButtonPublishTitle.defaultMessage }));
});
const publishButton = await screen.findByRole('button', { name: legacySidebarMessages.actionButtonPublishTitle.defaultMessage });
await user.click(publishButton);
axiosMock
.onPost(getXBlockBaseApiUrl(blockId), {