diff --git a/.github/workflows/add-issue-to-btr-project.yml b/.github/workflows/add-issue-to-btr-project.yml new file mode 100644 index 00000000..8b0ca1b3 --- /dev/null +++ b/.github/workflows/add-issue-to-btr-project.yml @@ -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 }} diff --git a/package-lock.json b/package-lock.json index 89ca93b0..93a4c117 100644 --- a/package-lock.json +++ b/package-lock.json @@ -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", diff --git a/src/course-home/outline-tab/DateSummary.jsx b/src/course-home/outline-tab/DateSummary.jsx index edff43a7..d1e5cbc7 100644 --- a/src/course-home/outline-tab/DateSummary.jsx +++ b/src/course-home/outline-tab/DateSummary.jsx @@ -65,6 +65,7 @@ const DateSummary = ({ )} {!linkedTitle && dateBlock.link && ( {}} className="description-link" diff --git a/src/courseware/course/sequence/Sequence.jsx b/src/courseware/course/sequence/Sequence.jsx index 6c56b219..e0b3c0a8 100644 --- a/src/courseware/course/sequence/Sequence.jsx +++ b/src/courseware/course/sequence/Sequence.jsx @@ -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 = ({
{!isEnabledOutlineSidebar && (
- { diff --git a/src/courseware/course/sidebar/sidebars/course-outline/CourseOutlineTray.jsx b/src/courseware/course/sidebar/sidebars/course-outline/CourseOutlineTray.jsx index a2ebfaf0..8cd17712 100644 --- a/src/courseware/course/sidebar/sidebars/course-outline/CourseOutlineTray.jsx +++ b/src/courseware/course/sidebar/sidebars/course-outline/CourseOutlineTray.jsx @@ -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(); diff --git a/src/index.scss b/src/index.scss index f4ae867e..fe517831 100755 --- a/src/index.scss +++ b/src/index.scss @@ -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, diff --git a/src/plugin-slots/README.md b/src/plugin-slots/README.md index 6444bbaf..be410164 100644 --- a/src/plugin-slots/README.md +++ b/src/plugin-slots/README.md @@ -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/) diff --git a/src/plugin-slots/SequenceNavigationSlot/README.md b/src/plugin-slots/SequenceNavigationSlot/README.md new file mode 100644 index 00000000..b8681625 --- /dev/null +++ b/src/plugin-slots/SequenceNavigationSlot/README.md @@ -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 +![Sequence navigation slot with default content](./screenshot_default.png) + +### Replaced with custom component +![📖 in sequence navigation slot](./screenshot_custom.png) + +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 ( + + + + {units.map((unit, index) => ( + + ))} + + + + ) + }, + }, + }, + ] + } + }, +} + +export default config; +``` diff --git a/src/plugin-slots/SequenceNavigationSlot/index.jsx b/src/plugin-slots/SequenceNavigationSlot/index.jsx new file mode 100644 index 00000000..c48652a4 --- /dev/null +++ b/src/plugin-slots/SequenceNavigationSlot/index.jsx @@ -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, +}) => ( + + + +); + +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; diff --git a/src/plugin-slots/SequenceNavigationSlot/screenshot_custom.png b/src/plugin-slots/SequenceNavigationSlot/screenshot_custom.png new file mode 100644 index 00000000..b2bb10a9 Binary files /dev/null and b/src/plugin-slots/SequenceNavigationSlot/screenshot_custom.png differ diff --git a/src/plugin-slots/SequenceNavigationSlot/screenshot_default.png b/src/plugin-slots/SequenceNavigationSlot/screenshot_default.png new file mode 100644 index 00000000..036cca1c Binary files /dev/null and b/src/plugin-slots/SequenceNavigationSlot/screenshot_default.png differ