Skip to content

Commit

Permalink
feat: initial editor layout + ui
Browse files Browse the repository at this point in the history
  • Loading branch information
connorhaugh committed Oct 17, 2023
1 parent 564d724 commit 27b2c6f
Show file tree
Hide file tree
Showing 11 changed files with 349 additions and 0 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
import React from 'react';
import { modes } from './constants';
import { selectors, thunkActions } from '../../data/redux';

const BlockSettingsEditor = ({
selectionMode,
onSelectionModeChange,
selectionSettings,
blocksInSelectedLibrary,
onSelectionSettingsChange,
}) => {

const getSelectionSettings = () =>{
if (selectionMode === modes.all){
return (<></>)
}
if (selectionMode === modes.random){
return (
<>
<Form.Group>
<Form.Control
type="number"
value={selectionSettings.count}
onChange= {(e)=>onSelectionSettingsChange({
count: e.target.value,
...selectionSettings
})}
floatingLabel="How many blocks do you want to show the author?"
/>
</Form.Group>
<Form.Group>
<Form.Switch
checked={selectionSettings.showReset}
onChange={(e) => onSelectionSettingsChange({
showReset: e.target.checked,
...selectionSettings
})}
>
Show Reset Button
</Form.Switch>
<div className="x-small mt-2">
Determines whether a 'Reset Problems' button is shown, so users may reset their answers and reshuffle selected items.
</div>
</Form.Group>
{/* TODO: ADD CAPA FILTERING FOR V1 ONLY */}
</>
)
}
if (selectionMode === modes.selected){
return (
<>
{/*TODO: ADD BLOCK PICKER*/}
<p>Block Selection Can be Made by Saving the editor and clicking the "view" button or going here.</p>
{/* Opens library page in new window.*/}
<a>https://studio.edx.org/container/block-v1:edX+LA101+2022_Summer+type@library_content+block@6a0e7d3c67614ae78e28d575408624cf</a>
</>
)
}
}
return (
<div>
<Form.Group as={Col} controlId="formGridState">
<Form.Control floatingLabel="How Do You Want to Select Your Content?" as="select"
onChange={onSelectionModeChange}
value={selectionMode}
>
{
modes.values().map(mode => (<option value={mode.value}>{mode.description}</option>))
}
</Form.Control>
{getSelectionSettings()}
</Form.Group>
</div>
);
};

export const mapStateToProps = (state) => ({
selectionMode: selectors.library.selectionMode(state),
selectionSettings: selectors.library.selectionSettings(state),
blocksInSelectedLibrary: selectors.library.blocksInSelectedLibrary(state),
})

export const mapDispatchToProps = {
onSelectionModeChange: thunkActions.library.onSelectionModeChange,
onSelectionSettingsChange: thunkActions.library.onSelectionSettingsChange,
};

export default injectIntl(connect(mapStateToProps, mapDispatchToProps)(LibraryBlockPicker));
35 changes: 35 additions & 0 deletions src/editors/containers/library_contentEditor/LibrarySelector.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import React from 'react';
import { Dropdown } from '@edx/paragon';


const LibrarySelector = ({ libraries, selectedLibrary, onSelectLibrary }) => {
return (
<div>
<label>Select a Library: </label>
<Dropdown
id="librarySelector"
label=""
placeholder="Select a library"
options={libraries.map(library => ({
label: library.name,
value: library.id,
}))}
value={selectedLibrary}
onChange={onSelectLibrary}
/>
</div>
);
};

export const mapStateToProps = (state) => ({
selectionMode: selectors.library.libraries(state),
selectionSettings: selectors.library.selectedLibrary(state),
})

export const mapDispatchToProps = {
onSelectLibrary: reducers.library.onSelectLibrary,
};

export default injectIntl(connect(mapStateToProps, mapDispatchToProps)(LibrarySelector));


16 changes: 16 additions & 0 deletions src/editors/containers/library_contentEditor/constants.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import messages from "./messages"

export const modes = {
all: {
description: messages.modeAll,
value: 'all'
},
random: {
description: messages.modeRandom,
value: 'random'
},
selected: {
description: messages.modeSelected,
value: 'selected'
}
}
Empty file.
51 changes: 51 additions & 0 deletions src/editors/containers/library_contentEditor/data/reducers.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
import { createSlice } from '@reduxjs/toolkit';
import { modes } from '../constants';

const initialState = {
libraries: [],
selectedLibrary: null,
selectionMode: modes.all,
selectionSettings: {
count: false,
showReset: false,
},
blocksInSelectedLibrary: [],
};

const library = createSlice({
name: 'library',
initialState,
reducers: {
initialize: (state, { payload }) => ({
...state,
studioEndpointUrl: payload.studioEndpointUrl,
lmsEndpointUrl: payload.lmsEndpointUrl,
blockId: payload.blockId,
learningContextId: payload.learningContextId,
blockType: payload.blockType,
blockValue: null,
}),
onSelectLibrary: (state, {payload}) => ({
...state,
selectedLibrary: payload.selectedLibrary,
}),
onSelectionModeChange: (state, {payload}) => ({
...state,
selectionMode: payload.selectionMode,
}),
onSelectionSettingsChange: (state,{payload})=>({
...state,
selectionSettings:payload.selectionSettings,
}),
},
});

const actions = StrictDict(app.actions);

const { reducer } = library;

export {
actions,
initialState,
reducer,
};
20 changes: 20 additions & 0 deletions src/editors/containers/library_contentEditor/data/selectors.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import { createSelector } from 'reselect';
import { blockTypes } from '../../constants/app';
import * as urls from '../../services/cms/urls';
import * as module from './selectors';

export const appSelector = (state) => state.app;

const mkSimpleSelector = (cb) => createSelector([module.appSelector], cb);

export const simpleSelectors = {
libraries: mkSimpleSelector(app => app.libraries),
selectedLibrary: mkSimpleSelector(app => app.selectedLibrary),
selectionMode: mkSimpleSelector(app => app.selectionMode),
selectionSettings: mkSimpleSelector(app => app.selectionSettings),
blocksInSelectedLibrary: mkSimpleSelector(app => app.blocksInSelectedLibrary),
}

export default {
...simpleSelectors,
}
Empty file.
111 changes: 111 additions & 0 deletions src/editors/containers/library_contentEditor/index.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
/* eslint-disable import/extensions */
/* eslint-disable import/no-unresolved */
/**
* This is an example component for an xblock Editor
* It uses pre-existing components to handle the saving of a the result of a function into the xblock's data.
* To use run npm run-script addXblock <your>
*/

/* eslint-disable no-unused-vars */

import React, { useState } from 'react';
import { connect } from 'react-redux';
import PropTypes from 'prop-types';

import { Spinner } from '@edx/paragon';
import { injectIntl, intlShape } from '@edx/frontend-platform/i18n';

import EditorContainer from '../EditorContainer';
import * as module from '.';
import { actions, selectors } from '../../data/redux';
import { RequestKeys } from '../../data/constants/requests';
import LibrarySelector from './LibrarySelector';
import LibraryBlockPicker from './LibraryBlockPicker';

export const hooks = {
getContent: () => ({
some: 'content',
}),
};

export const thumbEditor = ({
onClose,
// redux app layer
blockValue,
lmsEndpointUrl,
blockFailed,
blockFinished,
initializeEditor,
// inject
intl,
}) =>
{
const libraries, setLibraries = useState([]);

useEffect(() => {
setLibraries(api.getLibraries(cmsEndpointUrl));
}, []);

return (
<EditorContainer
getContent={module.hooks.getContent}
onClose={onClose}
>
<div className="editor-body h-75 overflow-auto">
{!blockFinished
? (
<div className="text-center p-6">
<Spinner
animation="border"
className="m-3"
// Use a messages.js file for intl messages.
screenreadertext={intl.formatMessage('Loading Spinner')}
/>
</div>
)
: (
<div>
<LibrarySelector
libraries
/>
{
selected_library ?
(<LibraryBlockPicker
/>):
(<></>)
}
</div>
)}
</div>
</EditorContainer>
)};
thumbEditor.defaultProps = {
blockValue: null,
lmsEndpointUrl: null,
};
thumbEditor.propTypes = {
onClose: PropTypes.func.isRequired,
// redux
blockValue: PropTypes.shape({
data: PropTypes.shape({ data: PropTypes.string }),
}),
lmsEndpointUrl: PropTypes.string,
blockFailed: PropTypes.bool.isRequired,
blockFinished: PropTypes.bool.isRequired,
initializeEditor: PropTypes.func.isRequired,
// inject
intl: intlShape.isRequired,
};

export const mapStateToProps = (state) => ({
blockValue: selectors.app.blockValue(state),
lmsEndpointUrl: selectors.app.lmsEndpointUrl(state),
blockFailed: selectors.requests.isFailed(state, { requestKey: RequestKeys.fetchBlock }),
blockFinished: selectors.requests.isFinished(state, { requestKey: RequestKeys.fetchBlock }),
});

export const mapDispatchToProps = {
initializeEditor: actions.app.initializeEditor,
};

export default injectIntl(connect(mapStateToProps, mapDispatchToProps)(thumbEditor));
22 changes: 22 additions & 0 deletions src/editors/containers/library_contentEditor/messages.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import { defineMessages } from '@edx/frontend-platform/i18n';

const messages = defineMessages({
modeAll: {
id: 'authoring.library_content.mode.all',
defaultMessage: 'Show all content from the library to every learner',
description: 'mode of selecting content from a library to put in a course',
},
modeRandom: {
id: 'authoring.library_content.mode.random',
defaultMessage: 'Show X problems at random from the Library',
description: 'mode of selecting content from a library to put in a course',
},
modeSelected: {
id: 'authoring.library_content.mode.selected',
defaultMessage: 'Show a specfic portion of the library to all users',
description: 'mode of selecting content from a library to put in a course',
},

});

export default messages;
2 changes: 2 additions & 0 deletions src/editors/data/constants/app.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ export const blockTypes = StrictDict({
html: 'html',
video: 'video',
problem: 'problem',
library_content: 'library_content',

// ADDED_EDITORS GO BELOW
video_upload: 'video_upload',
game: 'game',
Expand Down
4 changes: 4 additions & 0 deletions src/editors/supportedEditors.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ import ProblemEditor from './containers/ProblemEditor';
import VideoUploadEditor from './containers/VideoUploadEditor';
import GameEditor from './containers/GameEditor';

import library_contentEditor from './containers/library_contentEditor'

// ADDED_EDITOR_IMPORTS GO HERE

import { blockTypes } from './data/constants/app';
Expand All @@ -13,6 +15,8 @@ const supportedEditors = {
[blockTypes.video]: VideoEditor,
[blockTypes.problem]: ProblemEditor,
[blockTypes.video_upload]: VideoUploadEditor,
[blockTypes.library_content]: library_contentEditor,

// ADDED_EDITORS GO BELOW
[blockTypes.game]: GameEditor,
};
Expand Down

0 comments on commit 27b2c6f

Please sign in to comment.