diff --git a/docs/decisions/0010-tour-structure-decisions.md b/docs/decisions/0010-tour-structure-decisions.md deleted file mode 100644 index 76e3329d..00000000 --- a/docs/decisions/0010-tour-structure-decisions.md +++ /dev/null @@ -1,32 +0,0 @@ -# Tour Structure Decisions - -## Compartmentalizing the tour objects -We created the directory `src/tours` in order to organize tours across the MFE. Each tour has its own JSX file where we -define a tour object to be passed to the `` component in ``. - -Although each tour is stored in a JSX file, the tour object itself is meant to be an `object` type. Thus, the structure -of each tour object is as follows: -```$xslt -// Note: this is a simplified version of a tour object - -const exampleTour = (enabled) => ({ - checkpoints: [], - enabled, - tourId: 'exampleTour', -}); -``` - -The reason we use a JSX file rather than a JS file is to allow for use of React components within the objects such as -``. - -## Implementing i18n in tour objects -The `` component ingests a single prop called `tours` which expects a list of objects. -Given the structure in which we organized tour objects, there were two considerations in working with i18n: -- You can't injectIntl into something that isn't a React component without considerable adjustments, -so using the familiar `{intl.formatMessage(messages.foo)}` syntax would not be possible. -- You can't return normal objects from a React component, only React elements. I.e. switching these from arrow functions - to React function based components would not be ideal because the `tours` prop expects objects. - -### Decision -We chose to use `` directly within the tour objects. We also created shared `` -components inside of `GenericTourFormattedMessages.jsx` for use across the tours. diff --git a/package-lock.json b/package-lock.json index 6c762443..90415324 100644 --- a/package-lock.json +++ b/package-lock.json @@ -4070,9 +4070,9 @@ } }, "@edx/paragon": { - "version": "19.0.0", - "resolved": "https://registry.npmjs.org/@edx/paragon/-/paragon-19.0.0.tgz", - "integrity": "sha512-JBGF+65shm2Mf4s72WMd7RtY045rPZTwxH5zuKpEaF8X3dJfG2NIK8qICOFirpCGQWHHAcYiEOR8AQM8RyzJsw==", + "version": "19.1.0", + "resolved": "https://registry.npmjs.org/@edx/paragon/-/paragon-19.1.0.tgz", + "integrity": "sha512-n+dO0vqo0tzcHywtjzKakI9VIRVqrbcm2WyPAEU2nru9WsZrQs0icwnVEKPkouWmsxPKfMNHepQf4l+P0CeXUg==", "requires": { "@fortawesome/fontawesome-svg-core": "^1.2.36", "@fortawesome/free-solid-svg-icons": "^5.15.4", diff --git a/package.json b/package.json index 1eadd621..2ce48036 100644 --- a/package.json +++ b/package.json @@ -36,7 +36,7 @@ "@edx/frontend-enterprise-utils": "1.1.1", "@edx/frontend-lib-special-exams": "1.15.5", "@edx/frontend-platform": "1.14.3", - "@edx/paragon": "19.0.0", + "@edx/paragon": "19.1.0", "@edx/frontend-component-header": "^2.4.2", "@fortawesome/fontawesome-svg-core": "1.2.36", "@fortawesome/free-brands-svg-icons": "5.15.4", diff --git a/src/index.scss b/src/index.scss index eb0a0a54..6145a902 100755 --- a/src/index.scss +++ b/src/index.scss @@ -378,7 +378,6 @@ @import "course-home/progress-tab/course-completion/CompletionDonutChart.scss"; @import "course-home/progress-tab/grades/course-grade/GradeBar.scss"; @import "courseware/course/course-exit/CourseRecommendations"; -@import "src/tour/Checkpoint.scss"; /** [MM-P2P] Experiment */ @import "experiments/mm-p2p/index.scss"; diff --git a/src/product-tours/ProductTours.jsx b/src/product-tours/ProductTours.jsx index acaa241a..89a1f988 100644 --- a/src/product-tours/ProductTours.jsx +++ b/src/product-tours/ProductTours.jsx @@ -3,8 +3,7 @@ import { useDispatch, useSelector } from 'react-redux'; import PropTypes from 'prop-types'; import { sendTrackEvent } from '@edx/frontend-platform/analytics'; import { getAuthenticatedUser } from '@edx/frontend-platform/auth'; - -import Tour from '../tour/Tour'; +import { ProductTour } from '@edx/paragon'; import abandonTour from './AbandonTour'; import coursewareTour from './CoursewareTour'; @@ -82,7 +81,7 @@ function ProductTours({ } }, [showNewUserCourseHomeTour]); - // The component cannot handle rendering multiple enabled tours at once. + // The component cannot handle rendering multiple enabled tours at once. // I.e. when adding new tours, beware that if multiple tours are enabled, // the first enabled tour in the following array will be the only one that renders. // The suggestion for populating these tour objects is to ensure only one tour is enabled at a time. @@ -142,7 +141,7 @@ function ProductTours({ return ( <> - { expect(global.location.href).toEqual(`http://localhost/course/${courseId}/${defaultSequenceBlock.id}/${unitBlocks[1].id}`); - const checkpoint = container.querySelectorAll('#checkpoint'); + const checkpoint = container.querySelectorAll('#pgn__checkpoint'); expect(checkpoint).toHaveLength(showCoursewareTour ? 1 : 0); }); }); diff --git a/src/tour/Checkpoint.jsx b/src/tour/Checkpoint.jsx deleted file mode 100644 index a54cca90..00000000 --- a/src/tour/Checkpoint.jsx +++ /dev/null @@ -1,139 +0,0 @@ -import React, { useEffect, useState } from 'react'; -import PropTypes from 'prop-types'; -import { useMediaQuery } from '@edx/paragon'; -import { createPopper } from '@popperjs/core'; - -import CheckpointActionRow from './CheckpointActionRow'; -import CheckpointBody from './CheckpointBody'; -import CheckpointBreadcrumbs from './CheckpointBreadcrumbs'; -import CheckpointTitle from './CheckpointTitle'; - -function Checkpoint({ - body, - index, - placement, - target, - title, - totalCheckpoints, - ...props -}) { - const [checkpointVisible, setCheckpointVisible] = useState(false); - const isMobile = useMediaQuery({ query: '(max-width: 768px)' }); - - useEffect(() => { - const targetElement = document.querySelector(target); - const checkpoint = document.querySelector('#checkpoint'); - if (targetElement && checkpoint) { - // Translate the Checkpoint to its target's coordinates - const checkpointPopper = createPopper(targetElement, checkpoint, { - placement: isMobile ? 'top' : placement, - modifiers: [ - { - name: 'arrow', - options: { - padding: 25, - }, - }, - { - name: 'offset', - options: { - offset: [0, 20], - }, - }, - { - name: 'preventOverflow', - options: { - padding: 20, - tetherOffset: 35, - }, - }, - ], - }); - setCheckpointVisible(true); - if (checkpointPopper) { - checkpointPopper.forceUpdate(); - } - } - }, [target, isMobile]); - - useEffect(() => { - if (checkpointVisible) { - const targetElement = document.querySelector(target); - let targetOffset = targetElement.getBoundingClientRect().top; - if ((targetOffset < 0) || (targetElement.getBoundingClientRect().bottom > window.innerHeight)) { - if (placement.includes('top')) { - if (targetOffset < 0) { - targetOffset *= -1; - } - targetOffset -= 280; - } else { - targetOffset -= 80; - } - - window.scrollTo({ - top: targetOffset, behavior: 'smooth', - }); - } - - const button = document.querySelector('#checkpoint-primary-button'); - button.focus(); - } - }, [target, checkpointVisible]); - const isLastCheckpoint = index + 1 === totalCheckpoints; - const isOnlyCheckpoint = totalCheckpoints === 1; - return ( -