diff --git a/.env.development b/.env.development index df083d4f..db5888fb 100644 --- a/.env.development +++ b/.env.development @@ -1,7 +1,7 @@ NODE_ENV='development' -PORT=8080 +PORT=2000 ACCESS_TOKEN_COOKIE_NAME='edx-jwt-cookie-header-payload' -BASE_URL='localhost:8080' +BASE_URL='localhost:2000' CREDENTIALS_BASE_URL='http://localhost:18150' CSRF_TOKEN_API_PATH='/csrf/api/v1/token' ECOMMERCE_BASE_URL='http://localhost:18130' diff --git a/src/example/ExamplePage.jsx b/src/example/ExamplePage.jsx deleted file mode 100644 index ec21e1b0..00000000 --- a/src/example/ExamplePage.jsx +++ /dev/null @@ -1,12 +0,0 @@ -import React from 'react'; - -export default function ExamplePage() { - return ( - - - Example Page - Hello world! - - - ); -} diff --git a/src/example/ExamplePage.test.jsx b/src/example/ExamplePage.test.jsx deleted file mode 100644 index 3bad6f31..00000000 --- a/src/example/ExamplePage.test.jsx +++ /dev/null @@ -1,5 +0,0 @@ -describe('example', () => { - it('will pass because it is an example', () => { - - }); -}); diff --git a/src/example/data/.gitkeep b/src/example/data/.gitkeep deleted file mode 100644 index e69de29b..00000000 diff --git a/src/example/data/README.rst b/src/example/data/README.rst deleted file mode 100644 index c4db0b68..00000000 --- a/src/example/data/README.rst +++ /dev/null @@ -1,4 +0,0 @@ -data folder -=========== - -This folder is the home for non-component files, such as redux reducers, actions, selectors, API client services, etc. See `Feature-based Application Organization `_. for more detail. \ No newline at end of file diff --git a/src/example/index.scss b/src/example/index.scss deleted file mode 100644 index e69de29b..00000000 diff --git a/src/index.jsx b/src/index.jsx index e3ade7ce..18b05f22 100755 --- a/src/index.jsx +++ b/src/index.jsx @@ -4,12 +4,13 @@ import { APP_INIT_ERROR, APP_READY, subscribe, initialize } from '@edx/frontend- import { AppProvider, ErrorPage } from '@edx/frontend-platform/react'; import React from 'react'; import ReactDOM from 'react-dom'; +import { Route, Switch } from 'react-router-dom'; import Header, { messages as headerMessages } from '@edx/frontend-component-header'; import Footer, { messages as footerMessages } from '@edx/frontend-component-footer'; import appMessages from './i18n'; -import ExamplePage from './example/ExamplePage'; +import LearningSequencePage from './learning-sequence/LearningSequencePage'; import './index.scss'; import './assets/favicon.ico'; @@ -18,7 +19,9 @@ subscribe(APP_READY, () => { ReactDOM.render( - + + + , document.getElementById('root'), diff --git a/src/index.scss b/src/index.scss index 4baa5c4f..ab4e5a33 100755 --- a/src/index.scss +++ b/src/index.scss @@ -1,6 +1,6 @@ @import '~@edx/paragon/scss/edx/theme.scss'; -@import './example/index.scss'; +@import './learning-sequence/index'; @import "~@edx/frontend-component-header/dist/index"; @import "~@edx/frontend-component-footer/dist/footer"; diff --git a/src/learning-sequence/LearningSequencePage.jsx b/src/learning-sequence/LearningSequencePage.jsx new file mode 100644 index 00000000..a4655f51 --- /dev/null +++ b/src/learning-sequence/LearningSequencePage.jsx @@ -0,0 +1,119 @@ +import React, { useState, useEffect, useCallback, useRef } from 'react'; +import PropTypes from 'prop-types'; +import { camelCaseObject, getConfig } from '@edx/frontend-platform'; +import { getAuthenticatedHttpClient } from '@edx/frontend-platform/auth'; +import { injectIntl, intlShape } from '@edx/frontend-platform/i18n'; + +import PageLoading from './PageLoading'; + +import messages from './messages'; + +function useApi(apiFunction, { + format = true, keepDataIfFailed = false, loadedIfFailed = false, refreshParams = [], +}) { + const [data, setData] = useState(null); + const [loading, setLoading] = useState(false); + const [loaded, setLoaded] = useState(false); + const [failed, setFailed] = useState(false); + const [error, setError] = useState(null); + + useEffect(() => { + setLoading(true); + apiFunction().then((response) => { + const result = format ? camelCaseObject(response.data) : response.data; + setData(result); + setLoaded(true); + setLoading(false); + setError(null); + setFailed(false); + }) + .catch((e) => { + if (keepDataIfFailed) { + setData(null); + } + setFailed(true); + setLoading(false); + if (loadedIfFailed) { + setLoaded(true); + } + setError(e); + }); + }, refreshParams); + + return { + data, + loading, + loaded, + failed, + error, + }; +} + +function LearningSequencePage(props) { + const iframeRef = useRef(null); + + const handleResizeIframe = useCallback(() => { + // TODO: This won't work because of crossdomain issues. Leaving here for reference once we're + // able to have the iFrame content publish resize events through postMessage + console.log('**** Resizing iframe...'); + const iframe = iframeRef.current; + const contentHeight = iframe.contentWindow.document.body.scrollHeight; + console.log(`**** Height is: ${contentHeight}`); + iframe.height = contentHeight + 20; + }); + + const { + data, + loading, + loaded, + } = useApi( + async () => getAuthenticatedHttpClient().get(`${getConfig().LMS_BASE_URL}/api/courses/v1/blocks/?course_id=course-v1%3AedX%2BDemoX%2BDemo_Course&username=staff&depth=all&block_types_filter=sequential&requested_fields=children`, {}), + { + keepDataIfFailed: false, + refreshParams: [ + props.match.params.courseRunId, + props.match.params.sequenceBlockId, + ], + }, + ); + + console.log(data); + + if (loading) { + return ( + + ); + } + + return ( + + + Learning Sequence Page + Hello world! + Params: {props.match.params.courseRunId}/{props.match.params.sequenceBlockId} + State: + {loaded && data.blocks ? ( + + ) : null} + + + ); +} + +LearningSequencePage.propTypes = { + match: PropTypes.shape({ + params: PropTypes.shape({ + courseRunId: PropTypes.string.isRequired, + sequenceBlockId: PropTypes.string.isRequired, + }).isRequired, + }).isRequired, + intl: intlShape.isRequired, +}; + +export default injectIntl(LearningSequencePage); diff --git a/src/learning-sequence/PageLoading.jsx b/src/learning-sequence/PageLoading.jsx new file mode 100644 index 00000000..1b1135dc --- /dev/null +++ b/src/learning-sequence/PageLoading.jsx @@ -0,0 +1,37 @@ +import React, { Component } from 'react'; +import PropTypes from 'prop-types'; + +export default class PageLoading extends Component { + renderSrMessage() { + if (!this.props.srMessage) { + return null; + } + + return ( + + {this.props.srMessage} + + ); + } + + render() { + return ( + + + + {this.renderSrMessage()} + + + + ); + } +} + +PageLoading.propTypes = { + srMessage: PropTypes.string.isRequired, +}; diff --git a/src/learning-sequence/index.scss b/src/learning-sequence/index.scss new file mode 100644 index 00000000..97c9813e --- /dev/null +++ b/src/learning-sequence/index.scss @@ -0,0 +1,4 @@ +iframe { + border: 0; + width: 100%; +} diff --git a/src/learning-sequence/messages.js b/src/learning-sequence/messages.js new file mode 100644 index 00000000..65564d18 --- /dev/null +++ b/src/learning-sequence/messages.js @@ -0,0 +1,16 @@ +import { defineMessages } from '@edx/frontend-platform/i18n'; + +const messages = defineMessages({ + 'learn.loading.learning.sequence': { + id: 'learn.loading.learning.sequence', + defaultMessage: 'Loading learning sequence...', + description: 'Message when learning sequence is being loaded', + }, + 'learn.loading.error': { + id: 'learn.loading.error', + defaultMessage: 'Error: {error}', + description: 'Message when learning sequence fails to load', + }, +}); + +export default messages;
Hello world!