Skip to content

Adding dropdown menu for choosing Edx endpoint #965

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 13 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
"@material/list": "^0.33.0",
"@material/menu": "^0.33.0",
"@material/radio": "^0.33.0",
"@material/select": "^0.33.0",
"@material/textfield": "^0.33.0",
"@material/toolbar": "^0.33.0",
"@material/typography": "^0.28.0",
Expand Down Expand Up @@ -66,7 +67,6 @@
"mini-css-extract-plugin": "^0.9.0",
"mocha": "^7.1.1",
"moment": "^2.19.3",
"node-sass": "^4.14.1",
"nyc": "^15.1.0",
"object.entries": "^1.1.1",
"postcss-loader": "^3.0.0",
Expand Down Expand Up @@ -94,8 +94,9 @@
"redux-thunk": "^2.2.0",
"rmwc": "^1.4.1",
"sanitize-filename": "^1.6.1",
"sass": "^1.42.1",
"sass-lint": "^1.10.2",
"sass-loader": "^8.0.2",
"sass-loader": "^10.1.1",
"sinon": "^9.0.2",
"style-loader": "^0.18.2",
"tar": "^2.2.2",
Expand Down
5 changes: 5 additions & 0 deletions static/js/actions/collectionUi.js
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,11 @@ export const setEdxCourseId = createAction(SET_EDX_COURSE_ID)
export const SET_SELECTED_VIDEO_KEY = qualifiedName("SET_SELECTED_VIDEO_KEY")
export const setSelectedVideoKey = createAction(SET_SELECTED_VIDEO_KEY)

export const SET_SELECTED_EDX_ENDPOINT = qualifiedName(
"SET_SELECTED_EDX_ENDPOINT"
)
export const setSelectedEdxEndpoint = createAction(SET_SELECTED_EDX_ENDPOINT)

export const SET_IS_NEW = qualifiedName("SET_IS_NEW")
export const setIsNew = createAction(SET_IS_NEW)

Expand Down
36 changes: 28 additions & 8 deletions static/js/components/dialogs/CollectionFormDialog.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import type { Dispatch } from "redux"
import Radio from "../material/Radio"
import Textfield from "../material/Textfield"
import Textarea from "../material/Textarea"
import Select from "../material/Select"

import Dialog from "../material/Dialog"

Expand Down Expand Up @@ -38,7 +39,8 @@ type DialogProps = {
collectionForm: CollectionFormState,
open: boolean,
hideDialog: Function,
isEdxCourseAdmin?: boolean
isEdxCourseAdmin?: boolean,
availableEdxEndpoints: Array<any>
}

export class CollectionFormDialog extends React.Component<*, void> {
Expand Down Expand Up @@ -90,6 +92,10 @@ export class CollectionFormDialog extends React.Component<*, void> {
const { dispatch } = this.props
dispatch(uiActions.setEdxCourseId(event.target.value))
}
setEdxEndpoint = (value: Object) => {
const { dispatch } = this.props
dispatch(uiActions.setSelectedEdxEndpoint(value))
}

submitForm = async () => {
const {
Expand All @@ -115,6 +121,7 @@ export class CollectionFormDialog extends React.Component<*, void> {
}
if (isEdxCourseAdmin) {
payload.edx_course_id = collectionForm.edxCourseId
payload.edx_endpoint = collectionForm.edxEndpoint
}

try {
Expand Down Expand Up @@ -172,11 +179,15 @@ export class CollectionFormDialog extends React.Component<*, void> {
hideDialog,
collectionForm,
collectionUi: { isNew, errors },
isEdxCourseAdmin
isEdxCourseAdmin,
collection
} = this.props

const title = isNew ? "Create a New Collection" : "Edit Collection"
const submitText = isNew ? "Create Collection" : "Save"
const availableEdxEndpoints =
collection && collection.available_edx_endpoints ?
collection.available_edx_endpoints :
[]

return (
<Dialog
Expand Down Expand Up @@ -280,29 +291,38 @@ export class CollectionFormDialog extends React.Component<*, void> {
</Radio>
</section>

{!!isEdxCourseAdmin && (
{!!isEdxCourseAdmin && [
<Textfield
key="edx-course-id"
label="edx Course ID"
id="edx-course-id"
onChange={this.setCollectionEdxCourseId}
value={collectionForm.edxCourseId || ""}
required={false}
validationMessage={errors ? errors.edx_course_id : ""}
/>,
<Select
key="edx-endpoint-select"
selectedEndpoint={collectionForm.edxEndpoint}
setSelectedEndpoint={this.setEdxEndpoint}
menuItems={availableEdxEndpoints}
/>
)}
]}
</div>
</Dialog>
)
}
}

export const mapStateToProps = (state: any) => {
const { collectionUi } = state

const { collectionUi, collections } = state
const collection =
collections.loaded && collections.data ? collections.data : null
const collectionForm = getCollectionForm(collectionUi)
return {
collectionUi,
collectionForm
collectionForm,
collection
}
}

Expand Down
8 changes: 5 additions & 3 deletions static/js/components/dialogs/CollectionFormDialog_test.js
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,8 @@ import {
showEditCollectionDialog,
CLEAR_COLLECTION_FORM,
SET_COLLECTION_FORM_ERRORS,
CLEAR_COLLECTION_ERRORS
CLEAR_COLLECTION_ERRORS,
setSelectedEdxEndpoint
} from "../../actions/collectionUi"
import * as toastActions from "../../actions/toast"
import { INITIAL_UI_STATE } from "../../reducers/collectionUi"
Expand Down Expand Up @@ -146,7 +147,6 @@ describe("CollectionFormDialog", () => {
// Calling click handler directly due to MDC limitations (can't use enzyme's 'simulate')
wrapper.find("Dialog").prop("onAccept")()
})

assert.equal(store.getState().collectionUi.errors, expectedError)
})

Expand All @@ -166,6 +166,7 @@ describe("CollectionFormDialog", () => {
store.dispatch(setCollectionDesc("new description"))
store.dispatch(setCollectionTitle("new title"))
store.dispatch(setEdxCourseId("edx-course-id"))
store.dispatch(setSelectedEdxEndpoint(1))

sandbox.stub(api, "getCollections").returns(Promise.resolve({}))
let apiStub, expectedActionTypes
Expand Down Expand Up @@ -204,7 +205,8 @@ describe("CollectionFormDialog", () => {
view_lists: expectedListRequestData,
admin_lists: expectedListRequestData,
edx_course_id: "edx-course-id",
is_logged_in_only: false
is_logged_in_only: false,
edx_endpoint: 1
}

if (isNew) {
Expand Down
86 changes: 86 additions & 0 deletions static/js/components/material/Select.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
// @flow
import React from "react"

import { MDCSelect } from "@material/select/dist/mdc.select"
import type { MenuItem } from "../../flow/uiTypes"

type SelectProps = {
open: boolean,
selectedEndpoint: number,
menuItems: Array<MenuItem>
}

export default class Select extends React.Component<*, void> {
select: null
selectRoot: ?HTMLElement

componentDidMount() {
const { setSelectedEndpoint } = this.props
this.select = new MDCSelect(this.selectRoot)
this.select.listen("MDCSelect:change", () => {
if (this.select) {
setSelectedEndpoint(this.select.value)
}
})
}
// eslint-disable-next-line react/no-deprecated
componentWillReceiveProps(nextProps: SelectProps) {
if (this.select && nextProps.selectedEndpoint) {
if (this.select.selectedIndex < 0) {
this.select.selectedIndex = nextProps.selectedEndpoint
}
}
}

componentWillUnmount() {
if (this.select) {
this.select.destroy()
}
}

render() {
const { menuItems, selectedEndpoint } = this.props
const isSelected = selectedEndpoint > 0

return (
<div
className="mdc-select mdc-select--box"
role="listbox"
ref={div => (this.selectRoot = div)}
>
<div className="mdc-select__surface" tabIndex="0">
<div
className={`mdc-select__label ${
isSelected ? "mdc-select__label--float-above" : ""
}`}
>
Select Edx Endpoint
</div>
<div className="mdc-select__selected-text" />
<div className="mdc-select__bottom-line" />
</div>
<div className="mdc-menu mdc-select__menu">
<ul className="mdc-list mdc-menu__items">
{menuItems.map(
item => (
<li
key={`${item.id}_item`}
className="mdc-list-item"
aria-selected={
selectedEndpoint && item.id === selectedEndpoint
}
role="option"
tabIndex="0"
id={item.id}
>
{item.name}
</li>
),
this
)}
</ul>
</div>
</div>
)
}
}
50 changes: 50 additions & 0 deletions static/js/components/material/Select_test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
// @flow
/* global SETTINGS: false */
import React from "react"
import { assert } from "chai"
import { mount } from "enzyme"
import sinon from "sinon"
import configureTestStore from "redux-asserts"
import { Provider } from "react-redux"
import rootReducer from "../../reducers"
import Select from "./Select"

describe("Select", () => {
let sandbox, store
beforeEach(() => {
sandbox = sinon.createSandbox()
store = configureTestStore(rootReducer)
SETTINGS.is_edx_course_admin = true
})

afterEach(() => {
sandbox.restore()
})

const renderSelect = async (props = {}) => {
return mount(
<Provider store={store}>
<Select {...props} />
</Provider>
)
}

it("select element is rendered with no selected edx endpoint", async () => {
const wrapper = await renderSelect({
selectedEndpoint: -1,
menuItems: []
})
const selectNode = wrapper.find(".mdc-select__label").at(0)
assert.isTrue(selectNode.text().startsWith("Select Edx Endpoint"))
assert.isNotTrue(selectNode.hasClass("mdc-select__label--float-above"))
})

it("select element is rendered with selected edx endpoint", async () => {
const wrapper = await renderSelect({
selectedEndpoint: 3,
menuItems: []
})
const selectNode = wrapper.find(".mdc-select__label").at(0)
assert.isTrue(selectNode.hasClass("mdc-select__label--float-above"))
})
})
29 changes: 17 additions & 12 deletions static/js/factories/collection.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,16 +8,21 @@ import type { Collection } from "../flow/collectionTypes"
export const makeCollection = (
collectionKey: string = casual.uuid
): Collection => ({
key: collectionKey,
created_at: casual.moment.format(),
title: casual.text,
description: casual.text,
videos: makeVideos(2),
video_count: 2,
view_lists: casual.array_of_words(2),
admin_lists: casual.array_of_words(2),
is_logged_in_only: false,
edx_course_id: casual.word,
is_admin: true,
is_edx_course_admin: true
key: collectionKey,
created_at: casual.moment.format(),
title: casual.text,
description: casual.text,
videos: makeVideos(2),
video_count: 2,
view_lists: casual.array_of_words(2),
admin_lists: casual.array_of_words(2),
is_logged_in_only: false,
edx_course_id: casual.word,
is_admin: true,
is_edx_course_admin: true,
edx_endpoint: 1,
available_edx_endpoints: [
{ id: 1, name: "My local edx" },
{ id: 2, name: "MITx online" }
]
})
7 changes: 5 additions & 2 deletions static/js/flow/collectionTypes.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,9 @@ export type CollectionListItem = {
admin_lists: Array<string>,
is_logged_in_only: boolean,
video_count: number,
edx_course_id: ?string
edx_course_id: ?string,
available_edx_endpoints: Array<any>,
edx_endpoint: number,
};

export type Collection = CollectionListItem & {
Expand All @@ -30,6 +32,7 @@ export type CollectionFormState = {
adminChoice: string,
adminLists: ?string,
edxCourseId: ?string,
edxEndpoint: ?number,
};

export type CollectionValidation = {
Expand All @@ -44,7 +47,7 @@ export type CollectionUiState = {
editCollectionForm: CollectionFormState,
isNew: boolean,
selectedVideoKey: ?string,
errors?: CollectionValidation
errors?: CollectionValidation,
};

export type CollectionsPage = {
Expand Down
Loading