Merge branch 'openedx:master' into master
This commit is contained in:
18
.github/workflows/add-issue-to-btr-project.yml
vendored
Normal file
18
.github/workflows/add-issue-to-btr-project.yml
vendored
Normal file
@@ -0,0 +1,18 @@
|
||||
# Run the workflow that adds new tickets that are labelled "release testing"
|
||||
# to the org-wide BTR project board
|
||||
|
||||
name: Add release testing issues to the BTR project board
|
||||
|
||||
on:
|
||||
issues:
|
||||
types: [labeled]
|
||||
# This workflow is triggered when an issue is labeled with 'release testing'.
|
||||
# It adds the issue to the BTR project and applies the 'needs triage' label
|
||||
# if it doesn't already have it.
|
||||
|
||||
jobs:
|
||||
handle-release-testing:
|
||||
uses: openedx/.github/.github/workflows/add-issue-to-btr-project.yml@master
|
||||
secrets:
|
||||
GITHUB_APP_ID: ${{ secrets.GRAPHQL_AUTH_APP_ID }}
|
||||
GITHUB_APP_PRIVATE_KEY: ${{ secrets.GRAPHQL_AUTH_APP_PEM }}
|
||||
18
package-lock.json
generated
18
package-lock.json
generated
@@ -7884,9 +7884,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/caniuse-lite": {
|
||||
"version": "1.0.30001718",
|
||||
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001718.tgz",
|
||||
"integrity": "sha512-AflseV1ahcSunK53NfEs9gFWgOEmzr0f+kaMFA4xiLZlr9Hzt7HxcSpIFcnNCUkz6R6dWKa54rUz3HUmI3nVcw==",
|
||||
"version": "1.0.30001720",
|
||||
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001720.tgz",
|
||||
"integrity": "sha512-Ec/2yV2nNPwb4DnTANEV99ZWwm3ZWfdlfkQbWSDDt+PsXEVYwlhPH8tdMaPunYTKKmz7AnHi2oNEi1GcmKCD8g==",
|
||||
"funding": [
|
||||
{
|
||||
"type": "opencollective",
|
||||
@@ -18018,9 +18018,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/prebuild-install/node_modules/tar-fs": {
|
||||
"version": "2.1.2",
|
||||
"resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-2.1.2.tgz",
|
||||
"integrity": "sha512-EsaAXwxmx8UB7FRKqeozqEPop69DXcmYwTQwXvyAPF352HJsPdkVhvTaDPYqfNgruveJIJy3TA2l+2zj8LJIJA==",
|
||||
"version": "2.1.3",
|
||||
"resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-2.1.3.tgz",
|
||||
"integrity": "sha512-090nwYJDmlhwFwEW3QQl+vaNnxsO2yVsd45eTKRBzSzu+hlb1w2K9inVq5b0ngXuLVqQ4ApvsUHHnu/zQNkWAg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"chownr": "^1.1.1",
|
||||
@@ -21186,9 +21186,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/tar-fs": {
|
||||
"version": "3.0.8",
|
||||
"resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-3.0.8.tgz",
|
||||
"integrity": "sha512-ZoROL70jptorGAlgAYiLoBLItEKw/fUxg9BSYK/dF/GAGYFJOJJJMvjPAKDJraCXFwadD456FCuvLWgfhMsPwg==",
|
||||
"version": "3.0.9",
|
||||
"resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-3.0.9.tgz",
|
||||
"integrity": "sha512-XF4w9Xp+ZQgifKakjZYmFdkLoSWd34VGKcsTCwlNWM7QG3ZbaxnTsaBwnjFZqHRf/rROxaR8rXnbtwdvaDI+lA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"pump": "^3.0.0",
|
||||
|
||||
@@ -65,6 +65,7 @@ const DateSummary = ({
|
||||
)}
|
||||
{!linkedTitle && dateBlock.link && (
|
||||
<a
|
||||
id={dateBlock.dateType === 'verified-upgrade-deadline' ? 'date-verified-upgrade-deadline' : ''}
|
||||
href={dateBlock.link}
|
||||
onClick={dateBlock.dateType === 'verified-upgrade-deadline' ? logVerifiedUpgradeClick : () => {}}
|
||||
className="description-link"
|
||||
|
||||
@@ -13,17 +13,18 @@ import SequenceExamWrapper from '@edx/frontend-lib-special-exams';
|
||||
import PageLoading from '@src/generic/PageLoading';
|
||||
import { useModel } from '@src/generic/model-store';
|
||||
import { useSequenceBannerTextAlert, useSequenceEntranceExamAlert } from '@src/alerts/sequence-alerts/hooks';
|
||||
import SequenceContainerSlot from '../../../plugin-slots/SequenceContainerSlot';
|
||||
import SequenceContainerSlot from '@src/plugin-slots/SequenceContainerSlot';
|
||||
import { CourseOutlineSidebarSlot } from '@src/plugin-slots/CourseOutlineSidebarSlot';
|
||||
import { CourseOutlineSidebarTriggerSlot } from '@src/plugin-slots/CourseOutlineSidebarTriggerSlot';
|
||||
import { NotificationsDiscussionsSidebarSlot } from '@src/plugin-slots/NotificationsDiscussionsSidebarSlot';
|
||||
import SequenceNavigationSlot from '@src/plugin-slots/SequenceNavigationSlot';
|
||||
|
||||
import { getCoursewareOutlineSidebarSettings } from '../../data/selectors';
|
||||
import CourseLicense from '../course-license';
|
||||
import { NotificationsDiscussionsSidebarSlot } from '../../../plugin-slots/NotificationsDiscussionsSidebarSlot';
|
||||
import messages from './messages';
|
||||
import HiddenAfterDue from './hidden-after-due';
|
||||
import { SequenceNavigation, UnitNavigation } from './sequence-navigation';
|
||||
import { UnitNavigation } from './sequence-navigation';
|
||||
import SequenceContent from './SequenceContent';
|
||||
import { CourseOutlineSidebarSlot } from '../../../plugin-slots/CourseOutlineSidebarSlot';
|
||||
import { CourseOutlineSidebarTriggerSlot } from '../../../plugin-slots/CourseOutlineSidebarTriggerSlot';
|
||||
|
||||
const Sequence = ({
|
||||
unitId,
|
||||
@@ -172,7 +173,7 @@ const Sequence = ({
|
||||
<div className="sequence w-100">
|
||||
{!isEnabledOutlineSidebar && (
|
||||
<div className="sequence-navigation-container">
|
||||
<SequenceNavigation
|
||||
<SequenceNavigationSlot
|
||||
sequenceId={sequenceId}
|
||||
unitId={unitId}
|
||||
nextHandler={() => {
|
||||
|
||||
@@ -7,7 +7,6 @@ import {
|
||||
ChevronLeft as ChevronLeftIcon,
|
||||
} from '@openedx/paragon/icons';
|
||||
|
||||
import { useModel } from '@src/generic/model-store';
|
||||
import { LOADING } from '@src/constants';
|
||||
import PageLoading from '@src/generic/PageLoading';
|
||||
import SidebarSection from './components/SidebarSection';
|
||||
@@ -35,13 +34,13 @@ const CourseOutlineTray = () => {
|
||||
sequences,
|
||||
} = useCourseOutlineSidebar();
|
||||
|
||||
const {
|
||||
sectionId: activeSectionId,
|
||||
} = useModel('sequences', activeSequenceId);
|
||||
|
||||
const resolvedSectionId = selectedSection
|
||||
|| Object.keys(sections).find(
|
||||
(sectionId) => sections[sectionId].sequenceIds.includes(activeSequenceId),
|
||||
);
|
||||
const sectionsIds = Object.keys(sections);
|
||||
const sequenceIds = sections[selectedSection || activeSectionId]?.sequenceIds || [];
|
||||
const backButtonTitle = sections[selectedSection || activeSectionId]?.title;
|
||||
const sequenceIds = sections[resolvedSectionId]?.sequenceIds || [];
|
||||
const backButtonTitle = sections[resolvedSectionId]?.title;
|
||||
|
||||
const handleBackToSectionLevel = () => {
|
||||
setDisplaySectionLevel();
|
||||
|
||||
@@ -328,6 +328,7 @@
|
||||
.unit-navigation {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
gap: 5px;
|
||||
max-width: 640px;
|
||||
margin: 0 auto;
|
||||
|
||||
@@ -344,27 +345,12 @@
|
||||
border-radius: 6px;
|
||||
}
|
||||
}
|
||||
|
||||
.next-button {
|
||||
flex-basis: 75%;
|
||||
|
||||
@media (max-width: -1 + map-get($grid-breakpoints, "sm")) {
|
||||
flex-basis: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
.previous-button {
|
||||
flex-basis: 25%;
|
||||
|
||||
@media (max-width: -1 + map-get($grid-breakpoints, "sm")) {
|
||||
flex-basis: 100%;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.top-unit-navigation {
|
||||
display: flex;
|
||||
max-width: 100%;
|
||||
gap: 5px;
|
||||
justify-content: flex-end;
|
||||
|
||||
.next-button,
|
||||
|
||||
@@ -23,4 +23,5 @@
|
||||
* [`org.openedx.frontend.learning.progress_tab_grade_breakdown.v1`](./ProgressTabGradeBreakdownSlot/)
|
||||
* [`org.openedx.frontend.learning.progress_tab_related_links.v1`](./ProgressTabRelatedLinksSlot/)
|
||||
* [`org.openedx.frontend.learning.sequence_container.v1`](./SequenceContainerSlot/)
|
||||
* [`org.openedx.frontend.learning.sequence_navigation.v1`](./SequenceNavigationSlot/)
|
||||
* [`org.openedx.frontend.learning.unit_title.v1`](./UnitTitleSlot/)
|
||||
|
||||
80
src/plugin-slots/SequenceNavigationSlot/README.md
Normal file
80
src/plugin-slots/SequenceNavigationSlot/README.md
Normal file
@@ -0,0 +1,80 @@
|
||||
# Sequence Navigation Slot
|
||||
|
||||
### Slot ID: `org.openedx.frontend.learning.sequence_navigation.v1`
|
||||
|
||||
### Props:
|
||||
* `sequenceId` (string) — Current sequence identifier
|
||||
* `unitId` (string) — Current unit identifier
|
||||
* `nextHandler` (function) — Handler for next navigation action
|
||||
* `onNavigate` (function) — Handler for direct unit navigation
|
||||
* `previousHandler` (function) — Handler for previous navigation action
|
||||
|
||||
## Description
|
||||
|
||||
This slot is used to replace/modify/hide the sequence navigation component that controls navigation between units within a course sequence.
|
||||
|
||||
## Example
|
||||
|
||||
### Default content
|
||||

|
||||
|
||||
### Replaced with custom component
|
||||

|
||||
|
||||
The following `env.config.jsx` will replace the sequence navigation with a custom implementation that uses all available props.
|
||||
|
||||
```js
|
||||
import { DIRECT_PLUGIN, PLUGIN_OPERATIONS } from '@openedx/frontend-plugin-framework';
|
||||
|
||||
const config = {
|
||||
pluginSlots: {
|
||||
'org.openedx.frontend.learning.sequence_navigation.v1': {
|
||||
keepDefault: false,
|
||||
plugins: [
|
||||
{
|
||||
op: PLUGIN_OPERATIONS.Insert,
|
||||
widget: {
|
||||
id: 'custom_sequence_navigation',
|
||||
type: DIRECT_PLUGIN,
|
||||
RenderWidget: ({ sequenceId, unitId, nextHandler, onNavigate, previousHandler }) => {
|
||||
// Mock unit data for demonstration
|
||||
const units = ['unit-1', 'unit-2', 'unit-3'];
|
||||
|
||||
return (
|
||||
<Stack gap={2} direction="horizontal" className="p-3 bg-light w-100">
|
||||
<Button
|
||||
className="flex-grow-1"
|
||||
onClick={previousHandler}
|
||||
>
|
||||
⬅️ Previous
|
||||
</Button>
|
||||
<Stack gap={2} direction="horizontal">
|
||||
{units.map((unit, index) => (
|
||||
<Button
|
||||
variant="outline-primary"
|
||||
key={unit}
|
||||
className={`btn btn-sm ${unitId === unit ? 'btn-primary' : 'btn-outline-secondary'}`}
|
||||
onClick={() => onNavigate(unit)}
|
||||
>
|
||||
{index + 1}
|
||||
</Button>
|
||||
))}
|
||||
</Stack>
|
||||
<Button
|
||||
className="flex-grow-1"
|
||||
onClick={nextHandler}
|
||||
>
|
||||
Next ➡️
|
||||
</Button>
|
||||
</Stack>
|
||||
)
|
||||
},
|
||||
},
|
||||
},
|
||||
]
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
export default config;
|
||||
```
|
||||
45
src/plugin-slots/SequenceNavigationSlot/index.jsx
Normal file
45
src/plugin-slots/SequenceNavigationSlot/index.jsx
Normal file
@@ -0,0 +1,45 @@
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { PluginSlot } from '@openedx/frontend-plugin-framework';
|
||||
|
||||
import { SequenceNavigation } from '../../courseware/course/sequence/sequence-navigation';
|
||||
|
||||
const SequenceNavigationSlot = ({
|
||||
sequenceId,
|
||||
unitId,
|
||||
nextHandler,
|
||||
onNavigate,
|
||||
previousHandler,
|
||||
}) => (
|
||||
<PluginSlot
|
||||
id="org.openedx.frontend.learning.sequence_navigation.v1"
|
||||
slotOptions={{
|
||||
mergeProps: true,
|
||||
}}
|
||||
pluginProps={{
|
||||
sequenceId,
|
||||
unitId,
|
||||
nextHandler,
|
||||
onNavigate,
|
||||
previousHandler,
|
||||
}}
|
||||
>
|
||||
<SequenceNavigation
|
||||
sequenceId={sequenceId}
|
||||
unitId={unitId}
|
||||
nextHandler={nextHandler}
|
||||
onNavigate={onNavigate}
|
||||
previousHandler={previousHandler}
|
||||
/>
|
||||
</PluginSlot>
|
||||
);
|
||||
|
||||
SequenceNavigationSlot.propTypes = {
|
||||
sequenceId: PropTypes.string.isRequired,
|
||||
unitId: PropTypes.string.isRequired,
|
||||
nextHandler: PropTypes.func.isRequired,
|
||||
onNavigate: PropTypes.func.isRequired,
|
||||
previousHandler: PropTypes.func.isRequired,
|
||||
};
|
||||
|
||||
export default SequenceNavigationSlot;
|
||||
BIN
src/plugin-slots/SequenceNavigationSlot/screenshot_custom.png
Normal file
BIN
src/plugin-slots/SequenceNavigationSlot/screenshot_custom.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 709 KiB |
BIN
src/plugin-slots/SequenceNavigationSlot/screenshot_default.png
Normal file
BIN
src/plugin-slots/SequenceNavigationSlot/screenshot_default.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 756 KiB |
Reference in New Issue
Block a user