fix: Nits on UX/UI in the new sidebars in course outline and unit page [FC-0114] (#2871)

- Fixes the open nits/issues listed in https://github.com/openedx/frontend-app-authoring/issues/2868
- Fixes the small nit in the back button in the import workflow: https://github.com/openedx/frontend-app-authoring/issues/2524#issuecomment-3849649651
- Fixes the small nit in the unsupported text in the import workflow: https://github.com/openedx/frontend-app-authoring/issues/2525#issuecomment-3862737018. This fix depends on https://github.com/openedx/openedx-platform/pull/38005
This commit is contained in:
Chris Chávez
2026-02-13 15:09:28 -05:00
committed by GitHub
parent 4d401a9c22
commit c39923fc81
15 changed files with 83 additions and 32 deletions

View File

@@ -327,7 +327,7 @@ const AddTabs = () => {
return (
<Tabs
variant="tabs"
className="my-2 d-flex justify-content-around"
className="mb-4 mx-n4.5"
id="add-content-tabs"
activeKey={key}
onSelect={setKey}

View File

@@ -48,7 +48,7 @@ export const SectionSidebar = ({ sectionId }: Props) => {
{sectionData?.hasChanges && <PublishButon onClick={handlePublish} />}
<Tabs
variant="tabs"
className="my-2 d-flex justify-content-around"
className="my-2 mx-n3.5"
id="add-content-tabs"
activeKey={tab}
onSelect={setTab}

View File

@@ -49,7 +49,7 @@ export const SubsectionSidebar = ({ subsectionId }: Props) => {
{subsectionData?.hasChanges && <PublishButon onClick={handlePublish} />}
<Tabs
variant="tabs"
className="my-2 d-flex justify-content-around"
className="my-2 mx-n3.5"
id="add-content-tabs"
activeKey={tab}
onSelect={setTab}

View File

@@ -70,7 +70,7 @@ export const UnitSidebar = ({ unitId }: Props) => {
</Stack>
<Tabs
variant="tabs"
className="my-2 d-flex justify-content-around"
className="my-2 mx-n3.5"
id="add-content-tabs"
activeKey={tab}
onSelect={setTab}

View File

@@ -339,7 +339,7 @@ export const AddSidebar = () => {
<SidebarSection>
<Tabs
id="unit-add-sidebar"
className="my-2 d-flex justify-content-around"
className="mb-2 mx-n4.5 mx-n3.5"
activeKey={currentTabKey}
onSelect={setCurrentTabKey}
>

View File

@@ -212,7 +212,7 @@ export const UnitInfoSidebar = () => {
/>
<Tabs
id="unit-info-sidebar-tabs"
className="my-2 d-flex justify-content-around"
className="my-2 mx-n3.5"
activeKey={currentTabKey}
onSelect={setCurrentTabKey}
>

View File

@@ -7,14 +7,12 @@ import {
IconButtonToggle,
Stack,
} from '@openedx/paragon';
import {
FormatIndentDecrease,
FormatIndentIncrease,
} from '@openedx/paragon/icons';
import { ResizableBox } from '@src/generic/resizable/Resizable';
import type { MessageDescriptor } from 'react-intl';
import messages from './messages';
import { CollapsedIcon } from './icons/CollapsedIcon';
import { ExpandedIcon } from './icons/ExpandedIcon';
export interface SidebarPage {
component: React.ComponentType;
@@ -88,10 +86,11 @@ export function Sidebar<T extends SidebarPages>({
const intl = useIntl();
const SidebarComponent = pages[currentPageKey].component;
const activeKey = isOpen ? currentPageKey : undefined;
return (
<Stack direction="horizontal" className="sidebar align-items-baseline ml-3" gap={2}>
{isOpen && !!currentPageKey && (
{(isOpen && !!currentPageKey) ? (
<ResizableBox>
<div className="sidebar-content p-3 bg-white border-right">
<Dropdown data-testid="sidebar-dropdown">
@@ -121,16 +120,18 @@ export function Sidebar<T extends SidebarPages>({
<SidebarComponent />
</div>
</ResizableBox>
) : (
<div className="min-vh-100 border" />
)}
<div className="sidebar-toggle" data-testid="sidebar-toggle">
<div className="sidebar-toggle p-1" data-testid="sidebar-toggle">
<IconButton
src={isOpen ? FormatIndentIncrease : FormatIndentDecrease}
src={isOpen ? ExpandedIcon : CollapsedIcon}
alt={intl.formatMessage(messages.toggle)}
onClick={toggle}
variant="primary"
/>
<IconButtonToggle
activeValue={currentPageKey}
activeValue={activeKey}
onChange={setCurrentPageKey}
>
{Object.entries(pages).map(([key, page]) => (
@@ -142,7 +143,7 @@ export function Sidebar<T extends SidebarPages>({
value={key}
src={page.icon}
alt={intl.formatMessage(page.title)}
className="rounded-iconbutton"
className="rounded-iconbutton my-2"
/>
))}
</IconButtonToggle>

View File

@@ -27,17 +27,20 @@ export const SidebarTitle = ({
}: SidebarTitleProps) => {
const intl = useIntl();
return (
<Stack direction="horizontal" gap={2} className="mb-3">
{onBackBtnClick && (
<IconButton
onClick={onBackBtnClick}
alt={intl.formatMessage(messages.backBtnText)}
src={ArrowBack}
size="inline"
/>
)}
<Icon src={icon} className="mr-2 text-primary" />
<h2 className="text-primary h3 mb-0">{title}</h2>
</Stack>
<>
<Stack direction="horizontal" gap={2} className="mb-3">
{onBackBtnClick && (
<IconButton
onClick={onBackBtnClick}
alt={intl.formatMessage(messages.backBtnText)}
src={ArrowBack}
size="inline"
/>
)}
<Icon src={icon} className="mr-2 text-primary" />
<h2 className="text-primary h3 mb-0">{title}</h2>
</Stack>
<hr className="border" style={{ marginLeft: '-1rem', marginRight: '-1rem' }} />
</>
);
};

View File

@@ -0,0 +1,14 @@
export const CollapsedIcon = (props) => (
<svg
xmlns="http://www.w3.org/2000/svg"
width={24}
height={24}
viewBox="0 0 24 24"
{...props}
>
<path
d="M3 18H16V16H3V18ZM3 13H13V11H3V13ZM3 6V8H16V6H3ZM21 15.59L17.42 12L21 8.41L19.59 7L14.59 12L19.59 17L21 15.59Z"
fill="currentColor"
/>
</svg>
);

View File

@@ -0,0 +1,14 @@
export const ExpandedIcon = (props) => (
<svg
xmlns="http://www.w3.org/2000/svg"
width={24}
height={24}
viewBox="0 0 24 24"
{...props}
>
<path
d="M21 6L8 6V8L21 8V6ZM21 11H11V13H21V11ZM21 18V16L8 16V18H21ZM3 8.41L6.58 12L3 15.59L4.41 17L9.41 12L4.41 7L3 8.41Z"
fill="currentColor"
/>
</svg>
);

View File

@@ -6,6 +6,25 @@
min-height: 100vh;
height: 100%;
max-height: 300vh;
/*
* Change the styles for tabs in all sidebars.
*
* The tabs occupy the entire width of the sidebar,
* and each tab shares the space equally.
* The clickable area of the tab is the entire
* area it occupies, not just the label.
*/
.pgn__tabs {
.nav-item {
flex: 1 1 0;
text-align: center;
}
.pgn__tab_more {
display: none;
}
}
}
.dropdown-toggle {

View File

@@ -120,12 +120,12 @@ describe('<LibraryHome />', () => {
{
sourceKey: 'block-v1:UNIX+UX2+2025_T2+type@library_content+block@test_lib_content',
targetKey: null,
unsupportedReason: 'The "library_content" XBlock (ID: "test_lib_content") has children, so it not supported in content libraries. It has 2 children blocks.',
unsupportedReason: 'The "library_content" XBlock (ID: "test_lib_content") has children, so it is not supported in content libraries. It has 2 children blocks.',
},
{
sourceKey: 'block-v1:UNIX+UX2+2025_T2+type@conditional+block@test_conditional',
targetKey: null,
unsupportedReason: 'The "conditional" XBlock (ID: "test_conditional") has children, so it not supported in content libraries. It has 2 children blocks.',
unsupportedReason: 'The "conditional" XBlock (ID: "test_conditional") has children, so it is not supported in content libraries. It has 2 children blocks.',
},
]);
(useGetContentHits as jest.Mock).mockReturnValue({

View File

@@ -68,7 +68,7 @@ export const mockGetModulestoreMigratedBlocksInfo = {
{
sourceKey: 'block-v1:UNIX+UX2+2025_T2+type@library_content+block@test_lib_content',
targetKey: null,
unsupportedReason: 'The "library_content" XBlock (ID: "test_lib_content") has children, so it not supported in content libraries. It has 2 children blocks.',
unsupportedReason: 'The "library_content" XBlock (ID: "test_lib_content") has children, so it is not supported in content libraries. It has 2 children blocks.',
},
{
sourceKey: 'block-v1:UNIX+UX2+2025_T2+type@html+block@1',

View File

@@ -159,7 +159,7 @@ describe('<ImportDetailsPage />', () => {
name: 'library_content',
})).toBeInTheDocument();
expect(await screen.findByRole('cell', {
name: /has children, so it not supported in content libraries/i,
name: /has children, so it is not supported in content libraries/i,
})).toBeInTheDocument();
const viewImportedContentBtn = screen.getByRole('button', {

View File

@@ -163,7 +163,7 @@ export const ImportStepperPage = () => {
</ActionRow>
) : (
<ActionRow className="d-flex justify-content-between">
<Button onClick={() => setCurrentStep('select-course')} variant="tertiary">
<Button variant="outline-primary" onClick={() => setCurrentStep('select-course')}>
<FormattedMessage {...messages.importCourseBack} />
</Button>
{importIsBlocked ? (