Skip to content

Commit

Permalink
feat: 'frontend-lib-content-components' into this repo
Browse files Browse the repository at this point in the history
  • Loading branch information
bradenmacdonald committed Aug 9, 2024
2 parents bb88101 + d3d5fe0 commit afa2317
Show file tree
Hide file tree
Showing 507 changed files with 53,000 additions and 0 deletions.
68 changes: 68 additions & 0 deletions src/editors/Editor.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
import React from 'react';
import { useDispatch } from 'react-redux';
import PropTypes from 'prop-types';
import { FormattedMessage } from '@edx/frontend-platform/i18n';

import messages from './messages';
import * as hooks from './hooks';

import supportedEditors from './supportedEditors';

export const Editor = ({
learningContextId,
blockType,
blockId,
lmsEndpointUrl,
studioEndpointUrl,
onClose,
returnFunction,
}) => {
const dispatch = useDispatch();
hooks.initializeApp({
dispatch,
data: {
blockId,
blockType,
learningContextId,
lmsEndpointUrl,
studioEndpointUrl,
},
});

const EditorComponent = supportedEditors[blockType];
return (
<div
className="d-flex flex-column"
>
<div
className="pgn__modal-fullscreen h-100"
role="dialog"
aria-label={blockType}
>
{(EditorComponent !== undefined)
? <EditorComponent {...{ onClose, returnFunction }} />
: <FormattedMessage {...messages.couldNotFindEditor} />}
</div>
</div>
);
};
Editor.defaultProps = {
blockId: null,
learningContextId: null,
lmsEndpointUrl: null,
onClose: null,
returnFunction: null,
studioEndpointUrl: null,
};

Editor.propTypes = {
blockId: PropTypes.string,
blockType: PropTypes.string.isRequired,
learningContextId: PropTypes.string,
lmsEndpointUrl: PropTypes.string,
onClose: PropTypes.func,
returnFunction: PropTypes.func,
studioEndpointUrl: PropTypes.string,
};

export default Editor;
54 changes: 54 additions & 0 deletions src/editors/Editor.test.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
import React from 'react';
import { useDispatch } from 'react-redux';
import { shallow } from '@edx/react-unit-test-utils';
import { Editor } from './Editor';
import supportedEditors from './supportedEditors';
import * as hooks from './hooks';
import { blockTypes } from './data/constants/app';

jest.mock('./hooks', () => ({
initializeApp: jest.fn(),
}));

jest.mock('./containers/TextEditor', () => 'TextEditor');
jest.mock('./containers/VideoEditor', () => 'VideoEditor');
jest.mock('./containers/ProblemEditor', () => 'ProblemEditor');

const initData = {
blockId: 'block-v1:edX+DemoX+Demo_Course+type@html+block@030e35c4756a4ddc8d40b95fbbfff4d4',
blockType: blockTypes.html,
learningContextId: 'course-v1:edX+DemoX+Demo_Course',
lmsEndpointUrl: 'evenfakerurl.com',
studioEndpointUrl: 'fakeurl.com',
};
const props = {
initialize: jest.fn(),
onClose: jest.fn().mockName('props.onClose'),
courseId: 'course-v1:edX+DemoX+Demo_Course',
...initData,
};

let el;
describe('Editor', () => {
describe('render', () => {
test('snapshot: renders correct editor given blockType (html -> TextEditor)', () => {
expect(shallow(<Editor {...props} />).snapshot).toMatchSnapshot();
});
test('presents error message if no relevant editor found and ref ready', () => {
expect(shallow(<Editor {...props} blockType="fAkEBlock" />).snapshot).toMatchSnapshot();
});
test.each(Object.values(blockTypes))('renders %p editor when ref is ready', (blockType) => {
el = shallow(<Editor {...props} blockType={blockType} />);
expect(el.shallowWrapper.props.children.props.children.type).toBe(supportedEditors[blockType]);
});
});
describe('behavior', () => {
it('calls initializeApp hook with dispatch, and passed data', () => {
el = shallow(<Editor {...props} blockType={blockTypes.html} />);
expect(hooks.initializeApp).toHaveBeenCalledWith({
dispatch: useDispatch(),
data: initData,
});
});
});
});
58 changes: 58 additions & 0 deletions src/editors/EditorPage.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
import React from 'react';
import PropTypes from 'prop-types';
import { Provider } from 'react-redux';

import store from './data/store';
import Editor from './Editor';
import ErrorBoundary from './sharedComponents/ErrorBoundary';

export const EditorPage = ({
courseId,
blockType,
blockId,
lmsEndpointUrl,
studioEndpointUrl,
onClose,
returnFunction,
}) => (
<Provider store={store}>
<ErrorBoundary
{...{
learningContextId: courseId,
studioEndpointUrl,
}}
>
<Editor
{...{
onClose,
learningContextId: courseId,
blockType,
blockId,
lmsEndpointUrl,
studioEndpointUrl,
returnFunction,
}}
/>
</ErrorBoundary>
</Provider>
);
EditorPage.defaultProps = {
blockId: null,
courseId: null,
lmsEndpointUrl: null,
onClose: null,
returnFunction: null,
studioEndpointUrl: null,
};

EditorPage.propTypes = {
blockId: PropTypes.string,
blockType: PropTypes.string.isRequired,
courseId: PropTypes.string,
lmsEndpointUrl: PropTypes.string,
onClose: PropTypes.func,
returnFunction: PropTypes.func,
studioEndpointUrl: PropTypes.string,
};

export default EditorPage;
32 changes: 32 additions & 0 deletions src/editors/EditorPage.test.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import React from 'react';
import { shallow } from '@edx/react-unit-test-utils';
import EditorPage from './EditorPage';

const props = {
courseId: 'course-v1:edX+DemoX+Demo_Course',
blockType: 'html',
blockId: 'block-v1:edX+DemoX+Demo_Course+type@html+block@030e35c4756a4ddc8d40b95fbbfff4d4',
lmsEndpointUrl: 'evenfakerurl.com',
studioEndpointUrl: 'fakeurl.com',
onClose: jest.fn().mockName('props.onClose'),
};
jest.mock('react-redux', () => ({
Provider: 'Provider',
connect: (mapStateToProps, mapDispatchToProps) => (component) => ({
mapStateToProps,
mapDispatchToProps,
component,
}),
}));
jest.mock('./Editor', () => 'Editor');

describe('Editor Page', () => {
describe('snapshots', () => {
test('rendering correctly with expected Input', () => {
expect(shallow(<EditorPage {...props} />).snapshot).toMatchSnapshot();
});
test('props besides blockType default to null', () => {
expect(shallow(<EditorPage blockType={props.blockType} />).snapshot).toMatchSnapshot();
});
});
});
13 changes: 13 additions & 0 deletions src/editors/Placeholder.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import React from 'react';

const Placeholder = () => (
<div className="Placeholder">
<h1>
Under Construction
<br />
Coming Soon
</h1>
</div>
);

export default Placeholder;
36 changes: 36 additions & 0 deletions src/editors/Placeholder.test.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import React from 'react';
import { IntlProvider } from '@edx/frontend-platform/i18n';
import TestRenderer from 'react-test-renderer';
import { AppContext } from '@edx/frontend-platform/react';
import { Context as ResponsiveContext } from 'react-responsive';

import Placeholder from '../index';

describe('<Placeholder />', () => {
it('renders correctly', () => {
const component = (
<ResponsiveContext.Provider value={{ width: 1280 }}>
<IntlProvider locale="en" messages={{}}>
<AppContext.Provider
value={{
authenticatedUser: null,
config: {
LMS_BASE_URL: process.env.LMS_BASE_URL,
SITE_NAME: process.env.SITE_NAME,
LOGIN_URL: process.env.LOGIN_URL,
LOGOUT_URL: process.env.LOGOUT_URL,
LOGO_URL: process.env.LOGO_URL,
},
}}
>
<Placeholder />
</AppContext.Provider>
</IntlProvider>
</ResponsiveContext.Provider>
);

const wrapper = TestRenderer.create(component);

expect(wrapper.toJSON()).toMatchSnapshot();
});
});
36 changes: 36 additions & 0 deletions src/editors/VideoSelector.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import React from 'react';
import { useDispatch } from 'react-redux';
import PropTypes from 'prop-types';
import VideoGallery from './containers/VideoGallery';
import * as hooks from './hooks';

export const VideoSelector = ({
blockId,
learningContextId,
lmsEndpointUrl,
studioEndpointUrl,
}) => {
const dispatch = useDispatch();
hooks.initializeApp({
dispatch,
data: {
blockId,
blockType: 'video',
learningContextId,
lmsEndpointUrl,
studioEndpointUrl,
},
});
return (
<VideoGallery />
);
};

VideoSelector.propTypes = {
blockId: PropTypes.string.isRequired,
learningContextId: PropTypes.string.isRequired,
lmsEndpointUrl: PropTypes.string.isRequired,
studioEndpointUrl: PropTypes.string.isRequired,
};

export default VideoSelector;
40 changes: 40 additions & 0 deletions src/editors/VideoSelector.test.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import React from 'react';
import { useDispatch } from 'react-redux';
import { shallow } from '@edx/react-unit-test-utils';
import * as hooks from './hooks';
import VideoSelector from './VideoSelector';

jest.mock('./hooks', () => ({
initializeApp: jest.fn(),
}));

jest.mock('./containers/VideoGallery', () => 'VideoGallery');

const props = {
blockId: 'block-v1:edX+DemoX+Demo_Course+type@html+block@030e35c4756a4ddc8d40b95fbbfff4d4',
learningContextId: 'course-v1:edX+DemoX+Demo_Course',
lmsEndpointUrl: 'evenfakerurl.com',
studioEndpointUrl: 'fakeurl.com',
};

const initData = {
blockType: 'video',
...props,
};

describe('Video Selector', () => {
describe('render', () => {
test('rendering correctly with expected Input', () => {
expect(shallow(<VideoSelector {...props} />).snapshot).toMatchSnapshot();
});
});
describe('behavior', () => {
it('calls initializeApp hook with dispatch, and passed data', () => {
shallow(<VideoSelector {...props} />);
expect(hooks.initializeApp).toHaveBeenCalledWith({
dispatch: useDispatch(),
data: initData,
});
});
});
});
47 changes: 47 additions & 0 deletions src/editors/VideoSelectorPage.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import React from 'react';
import { Provider } from 'react-redux';
import PropTypes from 'prop-types';
import ErrorBoundary from './sharedComponents/ErrorBoundary';
import VideoSelector from './VideoSelector';
import store from './data/store';

const VideoSelectorPage = ({
blockId,
courseId,
lmsEndpointUrl,
studioEndpointUrl,
}) => (
<Provider store={store}>
<ErrorBoundary
{...{
learningContextId: courseId,
studioEndpointUrl,
}}
>
<VideoSelector
{...{
blockId,
learningContextId: courseId,
lmsEndpointUrl,
studioEndpointUrl,
}}
/>
</ErrorBoundary>
</Provider>
);

VideoSelectorPage.defaultProps = {
blockId: null,
courseId: null,
lmsEndpointUrl: null,
studioEndpointUrl: null,
};

VideoSelectorPage.propTypes = {
blockId: PropTypes.string,
courseId: PropTypes.string,
lmsEndpointUrl: PropTypes.string,
studioEndpointUrl: PropTypes.string,
};

export default VideoSelectorPage;
Loading

0 comments on commit afa2317

Please sign in to comment.