Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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: 4 additions & 0 deletions src/course-outline/CourseOutline.scss
Original file line number Diff line number Diff line change
Expand Up @@ -12,3 +12,7 @@
.border-dashed {
border: dashed;
}

.bg-draft-status {
background-color: #F4B57B;
}
2 changes: 1 addition & 1 deletion src/course-outline/CourseOutline.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2498,7 +2498,7 @@ describe('<CourseOutline />', () => {
expect(btn).toBeInTheDocument();
expect(await screen.findByRole('link', { name: 'View live' })).toBeInTheDocument();
expect((await screen.findAllByRole('button', { name: 'Add' })).length).toEqual(2);
expect(await screen.findByRole('button', { name: 'More actions' })).toBeInTheDocument();
expect(await screen.findByRole('button', { name: 'Course info' })).toBeInTheDocument();
const user = userEvent.setup();
await user.click(btn);
expect(await screen.findByRole('button', { name: 'Expand all' })).toBeInTheDocument();
Expand Down
1 change: 1 addition & 0 deletions src/course-outline/data/slice.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ const initialState = {
},
videoSharingEnabled: false,
videoSharingOptions: VIDEO_SHARING_OPTIONS.perVideo,
hasChanges: false,
},
sectionsList: [],
isCustomRelativeDatesActive: false,
Expand Down
2 changes: 2 additions & 0 deletions src/course-outline/data/thunk.ts
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@ export function fetchCourseOutlineIndexQuery(courseId: string): (dispatch: any)
videoSharingOptions,
actions,
end,
hasChanges,
},
} = outlineIndex;
dispatch(fetchOutlineIndexSuccess(outlineIndex));
Expand All @@ -80,6 +81,7 @@ export function fetchCourseOutlineIndexQuery(courseId: string): (dispatch: any)
videoSharingOptions,
videoSharingEnabled,
endDate: end,
hasChanges,
}));
dispatch(updateCourseActions(actions));

Expand Down
16 changes: 10 additions & 6 deletions src/course-outline/data/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ export interface CourseStructure {
start: string,
end: string,
actions: XBlockActions,
hasChanges: boolean,
}

// TODO: Create interface for all `Object` fields in courseOutline
Expand Down Expand Up @@ -35,19 +36,22 @@ export interface CourseDetails {
description?: string;
}

export interface ChecklistType {
totalCourseLaunchChecks: number;
completedCourseLaunchChecks: number;
totalCourseBestPracticesChecks: number;
completedCourseBestPracticesChecks: number;
}

export interface CourseOutlineStatusBar {
courseReleaseDate: string;
endDate: string;
highlightsEnabledForMessaging: boolean;
isSelfPaced: boolean;
checklist: {
totalCourseLaunchChecks: number;
completedCourseLaunchChecks: number;
totalCourseBestPracticesChecks: number;
completedCourseBestPracticesChecks: number;
};
checklist: ChecklistType;
videoSharingEnabled: boolean;
videoSharingOptions: string;
hasChanges: boolean;
}

export interface CourseOutlineState {
Expand Down
11 changes: 3 additions & 8 deletions src/course-outline/header-navigations/HeaderActions.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,6 @@ describe('<HeaderActions />', () => {

expect(await screen.findByRole('button', { name: messages.addButton.defaultMessage })).toBeInTheDocument();
expect(await screen.findByRole('button', { name: messages.viewLiveButton.defaultMessage })).toBeInTheDocument();
expect(await screen.findByRole('button', { name: messages.moreActionsButtonAriaLabel.defaultMessage })).toBeInTheDocument();
});

it('calls the correct handlers when clicking buttons', async () => {
Expand All @@ -67,17 +66,13 @@ describe('<HeaderActions />', () => {
expect(await screen.findByRole('button', { name: messages.addButton.defaultMessage })).toBeDisabled();
});

it('should change pages using the dropdown button', async () => {
it('should show course info on click', async () => {
renderComponent();

// Click on the dropdown button
await userEvent.click(screen.getByRole('button', { name: 'More actions' }));

// Select the Help option
const helpButton = screen.getByRole('button', { name: 'Help' });
await userEvent.click(helpButton);
await userEvent.click(screen.getByRole('button', { name: 'Course info' }));

// Check if the current page change is called
expect(setCurrentPageKeyMock).toHaveBeenCalledWith('help');
expect(setCurrentPageKeyMock).toHaveBeenCalledWith('info');
});
});
50 changes: 19 additions & 31 deletions src/course-outline/header-navigations/HeaderActions.tsx
Original file line number Diff line number Diff line change
@@ -1,18 +1,16 @@
import { useIntl } from '@edx/frontend-platform/i18n';
import {
Button, Dropdown, Icon, OverlayTrigger, Stack, Tooltip,
Button, OverlayTrigger, Stack, Tooltip,
} from '@openedx/paragon';
import {
Add as IconAdd, FindInPage, ViewSidebar,
Add as IconAdd, FindInPage, InfoOutline,
} from '@openedx/paragon/icons';

import { OutlinePageErrors, XBlockActions } from '@src/data/types';
import type { SidebarPage } from '@src/generic/sidebar';

import { type OutlineSidebarPageKeys, useOutlineSidebarContext } from '../outline-sidebar/OutlineSidebarContext';
import { useOutlineSidebarContext } from '../outline-sidebar/OutlineSidebarContext';

import messages from './messages';
import { getOutlineSidebarPages } from '../outline-sidebar/sidebarPages';

export interface HeaderActionsProps {
actions: {
Expand All @@ -29,12 +27,27 @@ const HeaderActions = ({
}: HeaderActionsProps) => {
const intl = useIntl();
const { lmsLink } = actions;
const sidebarPages = getOutlineSidebarPages();

const { setCurrentPageKey } = useOutlineSidebarContext();

return (
<Stack direction="horizontal" gap={3}>
<OverlayTrigger
placement="bottom"
overlay={(
<Tooltip id={intl.formatMessage(messages.courseInfoButtonTooltip)}>
{intl.formatMessage(messages.courseInfoButtonTooltip)}
</Tooltip>
)}
>
<Button
iconBefore={InfoOutline}
onClick={() => setCurrentPageKey('info')}
variant="outline-primary"
>
{intl.formatMessage(messages.courseInfoButton)}
</Button>
</OverlayTrigger>
{courseActions.childAddable && (
<OverlayTrigger
placement="bottom"
Expand Down Expand Up @@ -71,31 +84,6 @@ const HeaderActions = ({
{intl.formatMessage(messages.viewLiveButton)}
</Button>
</OverlayTrigger>
<Dropdown>
<Dropdown.Toggle
id="dropdown-toggle-with-iconbutton"
as={Button}
variant="outline-primary"
aria-label={intl.formatMessage(messages.moreActionsButtonAriaLabel)}
>
<Icon src={ViewSidebar} />
</Dropdown.Toggle>
<Dropdown.Menu className="mt-1">
{Object.entries(sidebarPages).filter(([, page]) => !page.hideFromActionMenu)
.map(([key, page]: [OutlineSidebarPageKeys, SidebarPage]) => (
<Dropdown.Item
key={key}
onClick={() => setCurrentPageKey(key)}
>
<Stack direction="horizontal" gap={2}>
<Icon src={page.icon} />
{intl.formatMessage(page.title)}
</Stack>
</Dropdown.Item>
))}
</Dropdown.Menu>
</Dropdown>

</Stack>
);
};
Expand Down
14 changes: 10 additions & 4 deletions src/course-outline/header-navigations/messages.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,14 +34,20 @@ const messages = defineMessages({
id: 'course-authoring.course-outline.header-navigations.button.view-live',
defaultMessage: 'View live',
},
moreActionsButtonAriaLabel: {
id: 'course-authoring.course-outline.header-navigations.button.more-actions.aria-label',
defaultMessage: 'More actions',
description: 'More actions button aria label in course outline',
courseInfoButtonTooltip: {
id: 'course-authoring.course-outline.header-navigations.button.course.info.tooltip',
defaultMessage: 'Click to open course info in sidebar',
description: 'Tooltip text of course info button',
},
courseInfoButton: {
id: 'course-authoring.course-outline.header-navigations.button.course.info',
defaultMessage: 'Course info',
description: 'Course info button in course outline header',
},
viewLiveButtonTooltip: {
id: 'course-authoring.course-outline.header-navigations.button.view-live.tooltip',
defaultMessage: 'Click to open the courseware in the LMS in a new tab',
description: 'Tooltip text of view live button',
},
});

Expand Down
1 change: 0 additions & 1 deletion src/course-outline/outline-sidebar/sidebarPages.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,6 @@ export const getOutlineSidebarPages = (): OutlineSidebarPages => {
component: AddSidebar,
icon: Plus,
title: messages.sidebarButtonAdd,
hideFromActionMenu: true,
},
} satisfies OutlineSidebarPages;
};
1 change: 1 addition & 0 deletions src/course-outline/status-bar/LegacyStatusBar.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ const statusBarData: CourseOutlineStatusBar = {
highlightsEnabledForMessaging: true,
videoSharingEnabled: true,
videoSharingOptions: VIDEO_SHARING_OPTIONS.allOn,
hasChanges: true,
};

const queryClient = new QueryClient();
Expand Down
40 changes: 40 additions & 0 deletions src/course-outline/status-bar/StatusBar.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,16 @@ const statusBarData: CourseOutlineStatusBar = {
highlightsEnabledForMessaging: true,
videoSharingEnabled: true,
videoSharingOptions: VIDEO_SHARING_OPTIONS.allOn,
hasChanges: false,
};

jest.mock('@src/course-libraries/data/apiHooks', () => ({
useEntityLinksSummaryByDownstreamContext: () => ({
data: [{ readyToSyncCount: 2 }],
isLoading: false,
}),
}));

const renderComponent = (props?: Partial<StatusBarProps>) => render(
<StatusBar
courseId={courseId}
Expand Down Expand Up @@ -73,4 +81,36 @@ describe('<StatusBar />', () => {

expect(await screen.findByTestId('redux-provider')).toBeEmptyDOMElement();
});

it('renders unpublished badge', async () => {
renderComponent({
statusBarData: {
...statusBarData,
hasChanges: true,
},
});
expect(await screen.findByText('Unpublished Changes')).toBeInTheDocument();
});

it('renders library updates', async () => {
renderComponent();
expect(await screen.findByText('2 Library Updates')).toBeInTheDocument();
});

it('hides checklist if completed', async () => {
renderComponent({
statusBarData: {
...statusBarData,
checklist: {
totalCourseLaunchChecks: 5,
completedCourseLaunchChecks: 5,
totalCourseBestPracticesChecks: 4,
completedCourseBestPracticesChecks: 4,
},
},
});
// wait for render
expect(await screen.findByText('Feb 05, 2013 - Apr 09, 2013')).toBeInTheDocument();
expect(screen.queryByText(`9/9 ${messages.checklistCompleted.defaultMessage}`)).toBeNull();
});
});
Loading