Skip to content

Commit afa2317

Browse files
feat: 'frontend-lib-content-components' into this repo
2 parents bb88101 + d3d5fe0 commit afa2317

File tree

507 files changed

+53000
-0
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

507 files changed

+53000
-0
lines changed

src/editors/Editor.jsx

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
import React from 'react';
2+
import { useDispatch } from 'react-redux';
3+
import PropTypes from 'prop-types';
4+
import { FormattedMessage } from '@edx/frontend-platform/i18n';
5+
6+
import messages from './messages';
7+
import * as hooks from './hooks';
8+
9+
import supportedEditors from './supportedEditors';
10+
11+
export const Editor = ({
12+
learningContextId,
13+
blockType,
14+
blockId,
15+
lmsEndpointUrl,
16+
studioEndpointUrl,
17+
onClose,
18+
returnFunction,
19+
}) => {
20+
const dispatch = useDispatch();
21+
hooks.initializeApp({
22+
dispatch,
23+
data: {
24+
blockId,
25+
blockType,
26+
learningContextId,
27+
lmsEndpointUrl,
28+
studioEndpointUrl,
29+
},
30+
});
31+
32+
const EditorComponent = supportedEditors[blockType];
33+
return (
34+
<div
35+
className="d-flex flex-column"
36+
>
37+
<div
38+
className="pgn__modal-fullscreen h-100"
39+
role="dialog"
40+
aria-label={blockType}
41+
>
42+
{(EditorComponent !== undefined)
43+
? <EditorComponent {...{ onClose, returnFunction }} />
44+
: <FormattedMessage {...messages.couldNotFindEditor} />}
45+
</div>
46+
</div>
47+
);
48+
};
49+
Editor.defaultProps = {
50+
blockId: null,
51+
learningContextId: null,
52+
lmsEndpointUrl: null,
53+
onClose: null,
54+
returnFunction: null,
55+
studioEndpointUrl: null,
56+
};
57+
58+
Editor.propTypes = {
59+
blockId: PropTypes.string,
60+
blockType: PropTypes.string.isRequired,
61+
learningContextId: PropTypes.string,
62+
lmsEndpointUrl: PropTypes.string,
63+
onClose: PropTypes.func,
64+
returnFunction: PropTypes.func,
65+
studioEndpointUrl: PropTypes.string,
66+
};
67+
68+
export default Editor;

src/editors/Editor.test.jsx

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
import React from 'react';
2+
import { useDispatch } from 'react-redux';
3+
import { shallow } from '@edx/react-unit-test-utils';
4+
import { Editor } from './Editor';
5+
import supportedEditors from './supportedEditors';
6+
import * as hooks from './hooks';
7+
import { blockTypes } from './data/constants/app';
8+
9+
jest.mock('./hooks', () => ({
10+
initializeApp: jest.fn(),
11+
}));
12+
13+
jest.mock('./containers/TextEditor', () => 'TextEditor');
14+
jest.mock('./containers/VideoEditor', () => 'VideoEditor');
15+
jest.mock('./containers/ProblemEditor', () => 'ProblemEditor');
16+
17+
const initData = {
18+
blockId: 'block-v1:edX+DemoX+Demo_Course+type@html+block@030e35c4756a4ddc8d40b95fbbfff4d4',
19+
blockType: blockTypes.html,
20+
learningContextId: 'course-v1:edX+DemoX+Demo_Course',
21+
lmsEndpointUrl: 'evenfakerurl.com',
22+
studioEndpointUrl: 'fakeurl.com',
23+
};
24+
const props = {
25+
initialize: jest.fn(),
26+
onClose: jest.fn().mockName('props.onClose'),
27+
courseId: 'course-v1:edX+DemoX+Demo_Course',
28+
...initData,
29+
};
30+
31+
let el;
32+
describe('Editor', () => {
33+
describe('render', () => {
34+
test('snapshot: renders correct editor given blockType (html -> TextEditor)', () => {
35+
expect(shallow(<Editor {...props} />).snapshot).toMatchSnapshot();
36+
});
37+
test('presents error message if no relevant editor found and ref ready', () => {
38+
expect(shallow(<Editor {...props} blockType="fAkEBlock" />).snapshot).toMatchSnapshot();
39+
});
40+
test.each(Object.values(blockTypes))('renders %p editor when ref is ready', (blockType) => {
41+
el = shallow(<Editor {...props} blockType={blockType} />);
42+
expect(el.shallowWrapper.props.children.props.children.type).toBe(supportedEditors[blockType]);
43+
});
44+
});
45+
describe('behavior', () => {
46+
it('calls initializeApp hook with dispatch, and passed data', () => {
47+
el = shallow(<Editor {...props} blockType={blockTypes.html} />);
48+
expect(hooks.initializeApp).toHaveBeenCalledWith({
49+
dispatch: useDispatch(),
50+
data: initData,
51+
});
52+
});
53+
});
54+
});

src/editors/EditorPage.jsx

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
import React from 'react';
2+
import PropTypes from 'prop-types';
3+
import { Provider } from 'react-redux';
4+
5+
import store from './data/store';
6+
import Editor from './Editor';
7+
import ErrorBoundary from './sharedComponents/ErrorBoundary';
8+
9+
export const EditorPage = ({
10+
courseId,
11+
blockType,
12+
blockId,
13+
lmsEndpointUrl,
14+
studioEndpointUrl,
15+
onClose,
16+
returnFunction,
17+
}) => (
18+
<Provider store={store}>
19+
<ErrorBoundary
20+
{...{
21+
learningContextId: courseId,
22+
studioEndpointUrl,
23+
}}
24+
>
25+
<Editor
26+
{...{
27+
onClose,
28+
learningContextId: courseId,
29+
blockType,
30+
blockId,
31+
lmsEndpointUrl,
32+
studioEndpointUrl,
33+
returnFunction,
34+
}}
35+
/>
36+
</ErrorBoundary>
37+
</Provider>
38+
);
39+
EditorPage.defaultProps = {
40+
blockId: null,
41+
courseId: null,
42+
lmsEndpointUrl: null,
43+
onClose: null,
44+
returnFunction: null,
45+
studioEndpointUrl: null,
46+
};
47+
48+
EditorPage.propTypes = {
49+
blockId: PropTypes.string,
50+
blockType: PropTypes.string.isRequired,
51+
courseId: PropTypes.string,
52+
lmsEndpointUrl: PropTypes.string,
53+
onClose: PropTypes.func,
54+
returnFunction: PropTypes.func,
55+
studioEndpointUrl: PropTypes.string,
56+
};
57+
58+
export default EditorPage;

src/editors/EditorPage.test.jsx

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
import React from 'react';
2+
import { shallow } from '@edx/react-unit-test-utils';
3+
import EditorPage from './EditorPage';
4+
5+
const props = {
6+
courseId: 'course-v1:edX+DemoX+Demo_Course',
7+
blockType: 'html',
8+
blockId: 'block-v1:edX+DemoX+Demo_Course+type@html+block@030e35c4756a4ddc8d40b95fbbfff4d4',
9+
lmsEndpointUrl: 'evenfakerurl.com',
10+
studioEndpointUrl: 'fakeurl.com',
11+
onClose: jest.fn().mockName('props.onClose'),
12+
};
13+
jest.mock('react-redux', () => ({
14+
Provider: 'Provider',
15+
connect: (mapStateToProps, mapDispatchToProps) => (component) => ({
16+
mapStateToProps,
17+
mapDispatchToProps,
18+
component,
19+
}),
20+
}));
21+
jest.mock('./Editor', () => 'Editor');
22+
23+
describe('Editor Page', () => {
24+
describe('snapshots', () => {
25+
test('rendering correctly with expected Input', () => {
26+
expect(shallow(<EditorPage {...props} />).snapshot).toMatchSnapshot();
27+
});
28+
test('props besides blockType default to null', () => {
29+
expect(shallow(<EditorPage blockType={props.blockType} />).snapshot).toMatchSnapshot();
30+
});
31+
});
32+
});

src/editors/Placeholder.jsx

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
import React from 'react';
2+
3+
const Placeholder = () => (
4+
<div className="Placeholder">
5+
<h1>
6+
Under Construction
7+
<br />
8+
Coming Soon
9+
</h1>
10+
</div>
11+
);
12+
13+
export default Placeholder;

src/editors/Placeholder.test.jsx

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
import React from 'react';
2+
import { IntlProvider } from '@edx/frontend-platform/i18n';
3+
import TestRenderer from 'react-test-renderer';
4+
import { AppContext } from '@edx/frontend-platform/react';
5+
import { Context as ResponsiveContext } from 'react-responsive';
6+
7+
import Placeholder from '../index';
8+
9+
describe('<Placeholder />', () => {
10+
it('renders correctly', () => {
11+
const component = (
12+
<ResponsiveContext.Provider value={{ width: 1280 }}>
13+
<IntlProvider locale="en" messages={{}}>
14+
<AppContext.Provider
15+
value={{
16+
authenticatedUser: null,
17+
config: {
18+
LMS_BASE_URL: process.env.LMS_BASE_URL,
19+
SITE_NAME: process.env.SITE_NAME,
20+
LOGIN_URL: process.env.LOGIN_URL,
21+
LOGOUT_URL: process.env.LOGOUT_URL,
22+
LOGO_URL: process.env.LOGO_URL,
23+
},
24+
}}
25+
>
26+
<Placeholder />
27+
</AppContext.Provider>
28+
</IntlProvider>
29+
</ResponsiveContext.Provider>
30+
);
31+
32+
const wrapper = TestRenderer.create(component);
33+
34+
expect(wrapper.toJSON()).toMatchSnapshot();
35+
});
36+
});

src/editors/VideoSelector.jsx

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
import React from 'react';
2+
import { useDispatch } from 'react-redux';
3+
import PropTypes from 'prop-types';
4+
import VideoGallery from './containers/VideoGallery';
5+
import * as hooks from './hooks';
6+
7+
export const VideoSelector = ({
8+
blockId,
9+
learningContextId,
10+
lmsEndpointUrl,
11+
studioEndpointUrl,
12+
}) => {
13+
const dispatch = useDispatch();
14+
hooks.initializeApp({
15+
dispatch,
16+
data: {
17+
blockId,
18+
blockType: 'video',
19+
learningContextId,
20+
lmsEndpointUrl,
21+
studioEndpointUrl,
22+
},
23+
});
24+
return (
25+
<VideoGallery />
26+
);
27+
};
28+
29+
VideoSelector.propTypes = {
30+
blockId: PropTypes.string.isRequired,
31+
learningContextId: PropTypes.string.isRequired,
32+
lmsEndpointUrl: PropTypes.string.isRequired,
33+
studioEndpointUrl: PropTypes.string.isRequired,
34+
};
35+
36+
export default VideoSelector;

src/editors/VideoSelector.test.jsx

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
import React from 'react';
2+
import { useDispatch } from 'react-redux';
3+
import { shallow } from '@edx/react-unit-test-utils';
4+
import * as hooks from './hooks';
5+
import VideoSelector from './VideoSelector';
6+
7+
jest.mock('./hooks', () => ({
8+
initializeApp: jest.fn(),
9+
}));
10+
11+
jest.mock('./containers/VideoGallery', () => 'VideoGallery');
12+
13+
const props = {
14+
blockId: 'block-v1:edX+DemoX+Demo_Course+type@html+block@030e35c4756a4ddc8d40b95fbbfff4d4',
15+
learningContextId: 'course-v1:edX+DemoX+Demo_Course',
16+
lmsEndpointUrl: 'evenfakerurl.com',
17+
studioEndpointUrl: 'fakeurl.com',
18+
};
19+
20+
const initData = {
21+
blockType: 'video',
22+
...props,
23+
};
24+
25+
describe('Video Selector', () => {
26+
describe('render', () => {
27+
test('rendering correctly with expected Input', () => {
28+
expect(shallow(<VideoSelector {...props} />).snapshot).toMatchSnapshot();
29+
});
30+
});
31+
describe('behavior', () => {
32+
it('calls initializeApp hook with dispatch, and passed data', () => {
33+
shallow(<VideoSelector {...props} />);
34+
expect(hooks.initializeApp).toHaveBeenCalledWith({
35+
dispatch: useDispatch(),
36+
data: initData,
37+
});
38+
});
39+
});
40+
});

src/editors/VideoSelectorPage.jsx

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
import React from 'react';
2+
import { Provider } from 'react-redux';
3+
import PropTypes from 'prop-types';
4+
import ErrorBoundary from './sharedComponents/ErrorBoundary';
5+
import VideoSelector from './VideoSelector';
6+
import store from './data/store';
7+
8+
const VideoSelectorPage = ({
9+
blockId,
10+
courseId,
11+
lmsEndpointUrl,
12+
studioEndpointUrl,
13+
}) => (
14+
<Provider store={store}>
15+
<ErrorBoundary
16+
{...{
17+
learningContextId: courseId,
18+
studioEndpointUrl,
19+
}}
20+
>
21+
<VideoSelector
22+
{...{
23+
blockId,
24+
learningContextId: courseId,
25+
lmsEndpointUrl,
26+
studioEndpointUrl,
27+
}}
28+
/>
29+
</ErrorBoundary>
30+
</Provider>
31+
);
32+
33+
VideoSelectorPage.defaultProps = {
34+
blockId: null,
35+
courseId: null,
36+
lmsEndpointUrl: null,
37+
studioEndpointUrl: null,
38+
};
39+
40+
VideoSelectorPage.propTypes = {
41+
blockId: PropTypes.string,
42+
courseId: PropTypes.string,
43+
lmsEndpointUrl: PropTypes.string,
44+
studioEndpointUrl: PropTypes.string,
45+
};
46+
47+
export default VideoSelectorPage;

0 commit comments

Comments
 (0)