Skip to content
Open
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion src/course-outline/OutlineAddChildButtons.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import messages from './messages';
interface NewChildButtonsProps {
handleNewButtonClick: () => void;
handleUseFromLibraryClick: () => void;
onClickCard?: (e: React.MouseEvent) => void;
childType: ContainerType;
btnVariant?: string;
btnClasses?: string;
Expand All @@ -18,6 +19,7 @@ interface NewChildButtonsProps {
const OutlineAddChildButtons = ({
handleNewButtonClick,
handleUseFromLibraryClick,
onClickCard,
childType,
btnVariant = 'outline-primary',
btnClasses = 'mt-4 border-gray-500 rounded-0',
Expand Down Expand Up @@ -59,7 +61,7 @@ const OutlineAddChildButtons = ({
}

return (
<Stack direction="horizontal" gap={3}>
<Stack direction="horizontal" gap={3} onClick={onClickCard}>
<Button
className={btnClasses}
variant={btnVariant}
Expand Down
15 changes: 12 additions & 3 deletions src/course-outline/card-header/CardHeader.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import {
Icon,
IconButton,
IconButtonWithTooltip,
Stack,
useToggle,
} from '@openedx/paragon';
import {
Expand Down Expand Up @@ -48,6 +49,7 @@ interface CardHeaderProps {
onClickMoveUp: () => void;
onClickMoveDown: () => void;
onClickCopy?: () => void;
onClickCard?: (e: React.MouseEvent) => void;
titleComponent: ReactNode;
namePrefix: string;
proctoringExamConfigurationLink?: string,
Expand Down Expand Up @@ -90,6 +92,7 @@ const CardHeader = ({
onClickMoveUp,
onClickMoveDown,
onClickCopy,
onClickCard,
titleComponent,
namePrefix,
actions,
Expand Down Expand Up @@ -154,10 +157,16 @@ const CardHeader = ({

return (
<>
<div
{
/* This is a special case; we can skip accessibility here since the
{Container}Card handles that. This onClick allows the user to select the card
Comment on lines +161 to +162
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So can you "select" the card by tabbing onto it and pressing SPACE or something?

Also, this says Card handles that, but in this PR I see keyboard-related code is in SortableItem - is that what you're talking about or is that something else?

by clicking on white areas of this component. */
}
<div // eslint-disable-line jsx-a11y/no-static-element-interactions, jsx-a11y/click-events-have-key-events
className="item-card-header"
data-testid={`${namePrefix}-card-header`}
ref={cardHeaderRef}
onClick={onClickCard}
>
{isFormOpen ? (
<Form.Group className="m-0 w-75">
Expand All @@ -178,7 +187,7 @@ const CardHeader = ({
/>
</Form.Group>
) : (
<>
<Stack direction="horizontal" gap={2}>
{titleComponent}
<IconButtonWithTooltip
className="item-card-button-icon"
Expand All @@ -190,7 +199,7 @@ const CardHeader = ({
// @ts-ignore
disabled={isSaving}
/>
</>
</Stack>
)}
<div className="ml-auto d-flex">
{(isVertical || isSequential) && (
Expand Down
42 changes: 16 additions & 26 deletions src/course-outline/card-header/TitleButton.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
import { useIntl } from '@edx/frontend-platform/i18n';
import {
Button,
OverlayTrigger,
Tooltip,
IconButtonWithTooltip,
Stack,
} from '@openedx/paragon';
import {
ArrowDropDown as ArrowDownIcon,
Expand All @@ -29,32 +28,23 @@ const TitleButton = ({
const titleTooltipMessage = intl.formatMessage(messages.expandTooltip);

return (
<OverlayTrigger
placement="bottom"
overlay={(
<Tooltip
id={`${title}-${titleTooltipMessage}`}
>
{titleTooltipMessage}
</Tooltip>
)}
>
<Button
iconBefore={isExpanded ? ArrowDownIcon : ArrowRightIcon}
variant="tertiary"
<Stack direction="horizontal">
<IconButtonWithTooltip
src={isExpanded ? ArrowDownIcon : ArrowRightIcon}
data-testid={`${namePrefix}-card-header__expanded-btn`}
alt={title}
tooltipContent={<div>{titleTooltipMessage}</div>}
className="item-card-header__title-btn"
onClick={onTitleClick}
title={title}
>
<div className="mr-2">
{prefixIcon}
</div>
<span className={`${namePrefix}-card-title mb-0 truncate-1-line`}>
{title}
</span>
</Button>
</OverlayTrigger>
size="inline"
/>
<div className="mr-2">
{prefixIcon}
</div>
<span className={`${namePrefix}-card-title mb-0 truncate-1-line`}>
{title}
</span>
</Stack>
);
};

Expand Down
2 changes: 1 addition & 1 deletion src/course-outline/card-header/TitleLink.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ const TitleLink = ({
to={titleLink}
title={title}
>
<span className={`${namePrefix}-card-title mb-0 truncate-1-line text-left`}>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this change intentional?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry, I forgot to update it. I added f26179d to meet the requirement The edit icon should be aligned next to the title with small titles

<span className={`${namePrefix}-card-title mb-0 text-left`}>
{title}
</span>
</Button>
Expand Down
12 changes: 12 additions & 0 deletions src/course-outline/drag-helper/SortableItem.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ interface SortableItemProps {
isDraggable?: boolean;
children: React.ReactNode;
componentStyle?: object;
onClick?: (e: React.MouseEvent) => void;
}

const SortableItem = ({
Expand All @@ -30,6 +31,7 @@ const SortableItem = ({
componentStyle,
data,
children,
onClick,
}: SortableItemProps) => {
const intl = useIntl();
const {
Expand Down Expand Up @@ -66,8 +68,18 @@ const SortableItem = ({
return (
<Row
ref={setNodeRef}
tabIndex={onClick ? 0 : -1}
style={style}
className="mx-0"
onClick={onClick}
onKeyDown={(e) => {
if (!onClick) { return; }

if (e.key === 'Enter' || e.key === ' ') {
e.preventDefault();
onClick(e);
}
}}
>
<Col className="extend-margin px-0">
{children}
Expand Down
15 changes: 15 additions & 0 deletions src/course-outline/outline-sidebar/OutlineSidebarContext.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import {
useState,
} from 'react';
import { useIntl } from '@edx/frontend-platform/i18n';
import { getConfig } from '@edx/frontend-platform';
import { useToggle } from '@openedx/paragon';
import { HelpOutline, Info } from '@openedx/paragon/icons';

Expand All @@ -25,6 +26,8 @@ interface OutlineSidebarContextData {
open: () => void;
toggle: () => void;
sidebarPages: OutlineSidebarPages;
selectedContainerId?: string;
openContainerInfoSidebar: (containerId: string) => void;
}

const OutlineSidebarContext = createContext<OutlineSidebarContextData | undefined>(undefined);
Expand All @@ -35,6 +38,14 @@ export const OutlineSidebarProvider = ({ children }: { children?: React.ReactNod
const [currentPageKey, setCurrentPageKeyState] = useState<OutlineSidebarPageKeys>('info');
const [isOpen, open, , toggle] = useToggle(true);

const [selectedContainerId, setSelectedContainerId] = useState<string | undefined>();

const openContainerInfoSidebar = useCallback((containerId: string) => {
if (getConfig().ENABLE_COURSE_OUTLINE_NEW_DESIGN?.toString().toLowerCase() === 'true') {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: Should/could we put getConfig().ENABLE_COURSE_OUTLINE_NEW_DESIGN?.toString().toLowerCase() === 'true' into a constant or a helper function somewhere? That seems complicated to write all that out every time we check this setting.

It could even be a waffle flag, then you can use useWaffleFlags and people can test it on a course-by-course basis. Not saying it needs to be though, this is fine.

setSelectedContainerId(containerId);
}
}, [setSelectedContainerId]);

const setCurrentPageKey = useCallback((pageKey: OutlineSidebarPageKeys) => {
setCurrentPageKeyState(pageKey);
open();
Expand All @@ -61,6 +72,8 @@ export const OutlineSidebarProvider = ({ children }: { children?: React.ReactNod
isOpen,
open,
toggle,
selectedContainerId,
openContainerInfoSidebar,
}),
[
currentPageKey,
Expand All @@ -69,6 +82,8 @@ export const OutlineSidebarProvider = ({ children }: { children?: React.ReactNod
isOpen,
open,
toggle,
selectedContainerId,
openContainerInfoSidebar,
],
);

Expand Down
74 changes: 52 additions & 22 deletions src/course-outline/section-card/SectionCard.test.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
import { getConfig, setConfig } from '@edx/frontend-platform';
import {
act, fireEvent, initializeMocks, render, screen, waitFor, within,
} from '@src/testUtils';
import { XBlock } from '@src/data/types';
import SectionCard from './SectionCard';
import { OutlineSidebarProvider } from '../outline-sidebar/OutlineSidebarContext';

const mockUseAcceptLibraryBlockChanges = jest.fn();
const mockUseIgnoreLibraryBlockChanges = jest.fn();
Expand Down Expand Up @@ -82,28 +84,30 @@ const section = {
const onEditSectionSubmit = jest.fn();

const renderComponent = (props?: object, entry = '/course/:courseId') => render(
<SectionCard
section={section}
index={1}
canMoveItem={jest.fn()}
onOrderChange={jest.fn()}
onOpenPublishModal={jest.fn()}
onOpenHighlightsModal={jest.fn()}
onOpenDeleteModal={jest.fn()}
onOpenUnlinkModal={jest.fn()}
onOpenConfigureModal={jest.fn()}
onEditSectionSubmit={onEditSectionSubmit}
onDuplicateSubmit={jest.fn()}
isSectionsExpanded
onNewSubsectionSubmit={jest.fn()}
isSelfPaced={false}
isCustomRelativeDatesActive={false}
onAddSubsectionFromLibrary={jest.fn()}
resetScrollState={jest.fn()}
{...props}
>
<span>children</span>
</SectionCard>,
<OutlineSidebarProvider>
<SectionCard
section={section}
index={1}
canMoveItem={jest.fn()}
onOrderChange={jest.fn()}
onOpenPublishModal={jest.fn()}
onOpenHighlightsModal={jest.fn()}
onOpenDeleteModal={jest.fn()}
onOpenUnlinkModal={jest.fn()}
onOpenConfigureModal={jest.fn()}
onEditSectionSubmit={onEditSectionSubmit}
onDuplicateSubmit={jest.fn()}
isSectionsExpanded
onNewSubsectionSubmit={jest.fn()}
isSelfPaced={false}
isCustomRelativeDatesActive={false}
onAddSubsectionFromLibrary={jest.fn()}
resetScrollState={jest.fn()}
{...props}
>
<span>children</span>
</SectionCard>
</OutlineSidebarProvider>,
{
path: '/course/:courseId',
params: { courseId: '5' },
Expand All @@ -123,6 +127,32 @@ describe('<SectionCard />', () => {

expect(screen.getByTestId('section-card-header')).toBeInTheDocument();
expect(screen.getByTestId('section-card__content')).toBeInTheDocument();

// The card is not selected
const card = screen.getByTestId('section-card');
expect(card).not.toHaveClass('outline-card-selected');
});

it('render SectionCard component in selected state', () => {
setConfig({
...getConfig(),
ENABLE_COURSE_OUTLINE_NEW_DESIGN: 'true',
});
const { container } = renderComponent();

expect(screen.getByTestId('section-card-header')).toBeInTheDocument();

// The card is not selected
const card = screen.getByTestId('section-card');
expect(card).not.toHaveClass('outline-card-selected');

// Get the <Row> that contains the card and click it to select the card
const el = container.querySelector('div.row.mx-0') as HTMLInputElement;
expect(el).not.toBeNull();
fireEvent.click(el!);

// The card is selected
expect(card).toHaveClass('outline-card-selected');
});

it('expands/collapses the card when the expand button is clicked', () => {
Expand Down
Loading