Skip to content
Projects
Groups
Snippets
Help
This project
Loading...
Sign in / Register
Toggle navigation
E
edx-platform
Overview
Overview
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
edx
edx-platform
Commits
1b2d1858
Commit
1b2d1858
authored
Apr 14, 2014
by
Andy Armstrong
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Fix duplicate save and cancel buttons in Studio
STUD-1531 Also add support for refreshing the modal on custom save
parent
f1eefc11
Hide whitespace changes
Inline
Side-by-side
Showing
21 changed files
with
277 additions
and
192 deletions
+277
-192
cms/djangoapps/contentstore/views/item.py
+0
-4
cms/djangoapps/contentstore/views/tests/test_item.py
+0
-31
cms/static/coffee/spec/views/module_edit_spec.coffee
+0
-1
cms/static/coffee/src/views/module_edit.coffee
+0
-1
cms/static/coffee/src/xblock/cms.runtime.v1.coffee
+59
-57
cms/static/js/spec/views/modals/edit_xblock_spec.js
+50
-8
cms/static/js/spec/views/pages/container_spec.js
+24
-1
cms/static/js/spec/views/xblock_editor_spec.js
+0
-14
cms/static/js/spec_helpers/edit_helpers.js
+6
-10
cms/static/js/spec_helpers/modal_helpers.js
+1
-1
cms/static/js/views/modals/base_modal.js
+19
-3
cms/static/js/views/modals/edit_xblock.js
+48
-10
cms/static/js/views/xblock_editor.js
+30
-17
cms/static/sass/elements/_modal-window.scss
+18
-1
cms/templates/component.html
+0
-26
cms/templates/container.html
+0
-3
cms/templates/edit-tabs.html
+0
-2
cms/templates/js/basic-modal.underscore
+1
-1
cms/templates/js/mock/mock-xblock-editor-with-custom-buttons.underscore
+20
-0
cms/templates/unit.html
+0
-1
common/static/coffee/src/xblock/core.coffee
+1
-0
No files found.
cms/djangoapps/contentstore/views/item.py
View file @
1b2d1858
...
@@ -246,10 +246,6 @@ def xblock_view_handler(request, package_id, view_name, tag=None, branch=None, v
...
@@ -246,10 +246,6 @@ def xblock_view_handler(request, package_id, view_name, tag=None, branch=None, v
fragment
.
content
=
render_to_string
(
'component.html'
,
{
fragment
.
content
=
render_to_string
(
'component.html'
,
{
'preview'
:
fragment
.
content
,
'preview'
:
fragment
.
content
,
'label'
:
component
.
display_name
or
component
.
scope_ids
.
block_type
,
'label'
:
component
.
display_name
or
component
.
scope_ids
.
block_type
,
# Native XBlocks are responsible for persisting their own data,
# so they are also responsible for providing save/cancel buttons.
'show_save_cancel'
:
isinstance
(
component
,
xmodule
.
x_module
.
XModuleDescriptor
),
})
})
else
:
else
:
raise
Http404
raise
Http404
...
...
cms/djangoapps/contentstore/views/tests/test_item.py
View file @
1b2d1858
...
@@ -765,34 +765,3 @@ class TestComponentHandler(TestCase):
...
@@ -765,34 +765,3 @@ class TestComponentHandler(TestCase):
self
.
descriptor
.
handle
=
create_response
self
.
descriptor
.
handle
=
create_response
self
.
assertEquals
(
component_handler
(
self
.
request
,
self
.
usage_id
,
'dummy_handler'
)
.
status_code
,
status_code
)
self
.
assertEquals
(
component_handler
(
self
.
request
,
self
.
usage_id
,
'dummy_handler'
)
.
status_code
,
status_code
)
@ddt.ddt
class
TestNativeXBlock
(
ItemTest
):
"""
Test a "native" XBlock (not an XModule shim).
"""
@ddt.data
((
'problem'
,
True
),
(
'acid'
,
False
))
@ddt.unpack
def
test_save_cancel_buttons
(
self
,
category
,
include_buttons
):
"""
Native XBlocks handle their own persistence, so Studio
should not render Save/Cancel buttons for them.
"""
# Create the XBlock
resp
=
self
.
create_xblock
(
category
=
category
)
self
.
assertEqual
(
resp
.
status_code
,
200
)
native_loc
=
json
.
loads
(
resp
.
content
)[
'locator'
]
# Render the XBlock
view_url
=
'/xblock/{locator}/student_view'
.
format
(
locator
=
native_loc
)
resp
=
self
.
client
.
get
(
view_url
,
HTTP_ACCEPT
=
'application/json'
)
self
.
assertEqual
(
resp
.
status_code
,
200
)
# Check that the save and cancel buttons are hidden for native XBlocks,
# but shown for XModule shim XBlocks
resp_html
=
json
.
loads
(
resp
.
content
)[
'html'
]
assert_func
=
self
.
assertIn
if
include_buttons
else
self
.
assertNotIn
assert_func
(
'save-button'
,
resp_html
)
assert_func
(
'cancel-button'
,
resp_html
)
cms/static/coffee/spec/views/module_edit_spec.coffee
View file @
1b2d1858
...
@@ -25,7 +25,6 @@ define ["jquery", "js/spec_helpers/edit_helpers", "coffee/src/views/module_edit"
...
@@ -25,7 +25,6 @@ define ["jquery", "js/spec_helpers/edit_helpers", "coffee/src/views/module_edit"
</section>
</section>
</li>
</li>
</ul>
</ul>
<div class="edit-xblock-modal"/>
"""
"""
edit_helpers
.
installEditTemplates
(
true
);
edit_helpers
.
installEditTemplates
(
true
);
spyOn
(
$
,
'ajax'
).
andReturn
(
@
moduleData
)
spyOn
(
$
,
'ajax'
).
andReturn
(
@
moduleData
)
...
...
cms/static/coffee/src/views/module_edit.coffee
View file @
1b2d1858
...
@@ -47,7 +47,6 @@ define ["jquery", "underscore", "gettext", "xblock/runtime.v1",
...
@@ -47,7 +47,6 @@ define ["jquery", "underscore", "gettext", "xblock/runtime.v1",
clickEditButton
:
(
event
)
->
clickEditButton
:
(
event
)
->
event
.
preventDefault
()
event
.
preventDefault
()
modal
=
new
EditXBlockModal
({
modal
=
new
EditXBlockModal
({
el
:
$
(
'.edit-xblock-modal'
),
view
:
'student_view'
view
:
'student_view'
});
});
modal
.
edit
(
this
.
$el
,
self
.
model
,
{
refresh
:
_
.
bind
(
@
render
,
this
)
})
modal
.
edit
(
this
.
$el
,
self
.
model
,
{
refresh
:
_
.
bind
(
@
render
,
this
)
})
cms/static/coffee/src/xblock/cms.runtime.v1.coffee
View file @
1b2d1858
...
@@ -5,74 +5,76 @@ define [
...
@@ -5,74 +5,76 @@ define [
@
PreviewRuntime
=
{}
@
PreviewRuntime
=
{}
class
PreviewRuntime
.
v1
extends
XBlock
.
Runtime
.
v1
class
PreviewRuntime
.
v1
extends
XBlock
.
Runtime
.
v1
handlerUrl
:
(
element
,
handlerName
,
suffix
,
query
,
thirdparty
)
->
handlerUrl
:
(
element
,
handlerName
,
suffix
,
query
,
thirdparty
)
->
uri
=
URI
(
"/preview/xblock"
).
segment
(
$
(
element
).
data
(
'usage-id'
))
uri
=
URI
(
"/preview/xblock"
).
segment
(
$
(
element
).
data
(
'usage-id'
))
.
segment
(
'handler'
)
.
segment
(
'handler'
)
.
segment
(
handlerName
)
.
segment
(
handlerName
)
if
suffix
?
then
uri
.
segment
(
suffix
)
if
suffix
?
then
uri
.
segment
(
suffix
)
if
query
?
then
uri
.
search
(
query
)
if
query
?
then
uri
.
search
(
query
)
uri
.
toString
()
uri
.
toString
()
@
StudioRuntime
=
{}
@
StudioRuntime
=
{}
class
StudioRuntime
.
v1
extends
XBlock
.
Runtime
.
v1
class
StudioRuntime
.
v1
extends
XBlock
.
Runtime
.
v1
constructor
:
()
->
constructor
:
()
->
super
()
super
()
@
savingNotification
=
new
NotificationView
.
Mini
@
savingNotification
=
new
NotificationView
.
Mini
title
:
gettext
(
'Saving…'
)
title
:
gettext
(
'Saving…'
)
@
alert
=
new
NotificationView
.
Error
@
alert
=
new
NotificationView
.
Error
title
:
"OpenAssessment Save Error"
,
title
:
"OpenAssessment Save Error"
,
closeIcon
:
false
,
closeIcon
:
false
,
shown
:
false
shown
:
false
handlerUrl
:
(
element
,
handlerName
,
suffix
,
query
,
thirdparty
)
->
handlerUrl
:
(
element
,
handlerName
,
suffix
,
query
,
thirdparty
)
->
uri
=
URI
(
"/xblock"
).
segment
(
$
(
element
).
data
(
'usage-id'
))
uri
=
URI
(
"/xblock"
).
segment
(
$
(
element
).
data
(
'usage-id'
))
.
segment
(
'handler'
)
.
segment
(
'handler'
)
.
segment
(
handlerName
)
.
segment
(
handlerName
)
if
suffix
?
then
uri
.
segment
(
suffix
)
if
suffix
?
then
uri
.
segment
(
suffix
)
if
query
?
then
uri
.
search
(
query
)
if
query
?
then
uri
.
search
(
query
)
uri
.
toString
()
uri
.
toString
()
# Notify the Studio client-side runtime so it can update
# Notify the Studio client-side runtime so it can update
# the UI in a consistent way. Currently, this is used
# the UI in a consistent way. Currently, this is used
# for save / cancel when editing an XBlock.
# for save / cancel when editing an XBlock.
# Although native XBlocks should handle their own persistence,
# Although native XBlocks should handle their own persistence,
# Studio still needs to update the UI in a consistent way
# Studio still needs to update the UI in a consistent way
# (showing the "Saving..." notification, closing the modal editing dialog, etc.)
# (showing the "Saving..." notification, closing the modal editing dialog, etc.)
notify
:
(
name
,
data
)
->
notify
:
(
name
,
data
)
->
if
name
==
'save'
if
name
==
'save'
if
'state'
of
data
if
'state'
of
data
# Starting to save, so show the "Saving..." notification
# Starting to save, so show the "Saving..." notification
if
data
.
state
==
'start'
if
data
.
state
==
'start'
@
savingNotification
.
show
()
@
savingNotification
.
show
()
# Finished saving, so hide the "Saving..." notification
# Finished saving, so hide the "Saving..." notification
else
if
data
.
state
==
'end'
else
if
data
.
state
==
'end'
@
_hideAlerts
()
# Hide the editor *after* we finish saving in case there are validation
# Notify the modal that the save has completed so that it can hide itself
# errors that the user needs to correct.
# and then refresh the xblock.
@
_hideEditor
()
if
@
modal
@
modal
.
onSave
()
$
(
'.component.editing'
).
removeClass
(
'editing'
)
@
savingNotification
.
hide
()
@
savingNotification
.
hide
()
else
if
name
==
'cancel
'
else
if
name
==
'edit-modal-shown
'
@
_hideEditor
()
@
modal
=
data
else
if
name
==
'error'
else
if
name
==
'edit-modal-hidden'
if
'msg'
of
data
@
modal
=
null
@
alert
.
options
.
message
=
data
.
msg
@
alert
.
show
()
_hideEditor
:
()
->
else
if
name
==
'cancel'
# This will close all open component editors, which works
@
_hideAlerts
()
# if we assume that <= 1 are open at a time.
if
@
modal
el
=
$
(
'.component.editing'
)
@
modal
.
cancel
()
el
.
removeClass
(
'editing'
)
el
.
find
(
'.component-editor'
).
slideUp
(
150
)
ModalUtils
.
hideModalCover
()
# Hide any alerts that are being shown
else
if
name
==
'error'
if
@
alert
.
options
.
shown
if
'msg'
of
data
@
alert
.
hide
()
@
alert
.
options
.
message
=
data
.
msg
@
alert
.
show
()
_hideAlerts
:
()
->
# Hide any alerts that are being shown
if
@
alert
.
options
.
shown
@
alert
.
hide
()
cms/static/js/spec/views/modals/edit_xblock_spec.js
View file @
1b2d1858
...
@@ -5,9 +5,9 @@ define(["jquery", "underscore", "js/spec_helpers/create_sinon", "js/spec_helpers
...
@@ -5,9 +5,9 @@ define(["jquery", "underscore", "js/spec_helpers/create_sinon", "js/spec_helpers
describe
(
"EditXBlockModal"
,
function
()
{
describe
(
"EditXBlockModal"
,
function
()
{
var
model
,
modal
,
showModal
;
var
model
,
modal
,
showModal
;
showModal
=
function
(
requests
,
mockHtml
)
{
showModal
=
function
(
requests
,
mockHtml
,
options
)
{
var
xblockElement
=
$
(
'.xblock'
);
var
xblockElement
=
$
(
'.xblock'
);
return
edit_helpers
.
showEditModal
(
requests
,
xblockElement
,
model
,
mockHtml
);
return
edit_helpers
.
showEditModal
(
requests
,
xblockElement
,
model
,
mockHtml
,
options
);
};
};
beforeEach
(
function
()
{
beforeEach
(
function
()
{
...
@@ -45,6 +45,13 @@ define(["jquery", "underscore", "js/spec_helpers/create_sinon", "js/spec_helpers
...
@@ -45,6 +45,13 @@ define(["jquery", "underscore", "js/spec_helpers/create_sinon", "js/spec_helpers
expect
(
edit_helpers
.
isShowingModal
(
modal
)).
toBeFalsy
();
expect
(
edit_helpers
.
isShowingModal
(
modal
)).
toBeFalsy
();
});
});
it
(
'does not show the "Save" button'
,
function
()
{
var
requests
=
create_sinon
.
requests
(
this
);
modal
=
showModal
(
requests
,
mockXBlockEditorHtml
);
expect
(
modal
.
$
(
'.action-save'
)).
not
.
toBeVisible
();
expect
(
modal
.
$
(
'.action-cancel'
).
text
()).
toBe
(
'OK'
);
});
it
(
'shows the correct title'
,
function
()
{
it
(
'shows the correct title'
,
function
()
{
var
requests
=
create_sinon
.
requests
(
this
);
var
requests
=
create_sinon
.
requests
(
this
);
modal
=
showModal
(
requests
,
mockXBlockEditorHtml
);
modal
=
showModal
(
requests
,
mockXBlockEditorHtml
);
...
@@ -56,6 +63,43 @@ define(["jquery", "underscore", "js/spec_helpers/create_sinon", "js/spec_helpers
...
@@ -56,6 +63,43 @@ define(["jquery", "underscore", "js/spec_helpers/create_sinon", "js/spec_helpers
modal
=
showModal
(
requests
,
mockXBlockEditorHtml
);
modal
=
showModal
(
requests
,
mockXBlockEditorHtml
);
expect
(
modal
.
$
(
'.editor-modes a'
).
length
).
toBe
(
0
);
expect
(
modal
.
$
(
'.editor-modes a'
).
length
).
toBe
(
0
);
});
});
it
(
'hides itself and refreshes after save notification'
,
function
()
{
var
requests
=
create_sinon
.
requests
(
this
),
refreshed
=
false
,
refresh
=
function
()
{
refreshed
=
true
;
};
modal
=
showModal
(
requests
,
mockXBlockEditorHtml
,
{
refresh
:
refresh
});
modal
.
runtime
.
notify
(
'save'
,
{
state
:
'start'
});
modal
.
runtime
.
notify
(
'save'
,
{
state
:
'end'
});
expect
(
edit_helpers
.
isShowingModal
(
modal
)).
toBeFalsy
();
expect
(
refreshed
).
toBeTruthy
();
});
it
(
'hides itself and does not refresh after cancel notification'
,
function
()
{
var
requests
=
create_sinon
.
requests
(
this
),
refreshed
=
false
,
refresh
=
function
()
{
refreshed
=
true
;
};
modal
=
showModal
(
requests
,
mockXBlockEditorHtml
,
{
refresh
:
refresh
});
modal
.
runtime
.
notify
(
'cancel'
);
expect
(
edit_helpers
.
isShowingModal
(
modal
)).
toBeFalsy
();
expect
(
refreshed
).
toBeFalsy
();
});
describe
(
"Custom Buttons"
,
function
()
{
var
mockCustomButtonsHtml
;
mockCustomButtonsHtml
=
readFixtures
(
'mock/mock-xblock-editor-with-custom-buttons.underscore'
);
it
(
'hides the modal
\'
s button bar'
,
function
()
{
var
requests
=
create_sinon
.
requests
(
this
);
modal
=
showModal
(
requests
,
mockCustomButtonsHtml
);
expect
(
modal
.
$
(
'.modal-actions'
)).
toBeHidden
();
});
});
});
});
describe
(
"XModule Editor"
,
function
()
{
describe
(
"XModule Editor"
,
function
()
{
...
@@ -64,12 +108,11 @@ define(["jquery", "underscore", "js/spec_helpers/create_sinon", "js/spec_helpers
...
@@ -64,12 +108,11 @@ define(["jquery", "underscore", "js/spec_helpers/create_sinon", "js/spec_helpers
mockXModuleEditorHtml
=
readFixtures
(
'mock/mock-xmodule-editor.underscore'
);
mockXModuleEditorHtml
=
readFixtures
(
'mock/mock-xmodule-editor.underscore'
);
beforeEach
(
function
()
{
beforeEach
(
function
()
{
// Mock the VerticalDescriptor so that the module can be rendered
edit_helpers
.
installMockXModule
();
window
.
VerticalDescriptor
=
XModule
.
Descriptor
;
});
});
afterEach
(
function
()
{
afterEach
(
function
()
{
window
.
VerticalDescriptor
=
null
;
edit_helpers
.
uninstallMockXModule
()
;
});
});
it
(
'can render itself'
,
function
()
{
it
(
'can render itself'
,
function
()
{
...
@@ -140,12 +183,11 @@ define(["jquery", "underscore", "js/spec_helpers/create_sinon", "js/spec_helpers
...
@@ -140,12 +183,11 @@ define(["jquery", "underscore", "js/spec_helpers/create_sinon", "js/spec_helpers
mockXModuleEditorHtml
=
readFixtures
(
'mock/mock-xmodule-settings-only-editor.underscore'
);
mockXModuleEditorHtml
=
readFixtures
(
'mock/mock-xmodule-settings-only-editor.underscore'
);
beforeEach
(
function
()
{
beforeEach
(
function
()
{
// Mock the VerticalDescriptor so that the module can be rendered
edit_helpers
.
installMockXModule
();
window
.
VerticalDescriptor
=
XModule
.
Descriptor
;
});
});
afterEach
(
function
()
{
afterEach
(
function
()
{
window
.
VerticalDescriptor
=
null
;
edit_helpers
.
uninstallMockXModule
()
;
});
});
it
(
'can render itself'
,
function
()
{
it
(
'can render itself'
,
function
()
{
...
...
cms/static/js/spec/views/pages/container_spec.js
View file @
1b2d1858
...
@@ -107,6 +107,29 @@ define(["jquery", "js/spec_helpers/create_sinon", "js/spec_helpers/edit_helpers"
...
@@ -107,6 +107,29 @@ define(["jquery", "js/spec_helpers/create_sinon", "js/spec_helpers/edit_helpers"
});
});
expect
(
edit_helpers
.
isShowingModal
()).
toBeTruthy
();
expect
(
edit_helpers
.
isShowingModal
()).
toBeTruthy
();
});
});
});
describe
(
"Editing an xmodule"
,
function
()
{
var
mockContainerXBlockHtml
,
mockXModuleEditor
,
newDisplayName
=
'New Display Name'
;
beforeEach
(
function
()
{
edit_helpers
.
installMockXModule
({
data
:
"<p>Some HTML</p>"
,
metadata
:
{
display_name
:
newDisplayName
}
});
});
afterEach
(
function
()
{
edit_helpers
.
uninstallMockXModule
();
edit_helpers
.
cancelModalIfShowing
();
});
mockContainerXBlockHtml
=
readFixtures
(
'mock/mock-container-xblock.underscore'
);
mockXModuleEditor
=
readFixtures
(
'mock/mock-xmodule-editor.underscore'
);
it
(
'can save changes to settings'
,
function
()
{
it
(
'can save changes to settings'
,
function
()
{
var
editButtons
,
modal
,
mockUpdatedXBlockHtml
;
var
editButtons
,
modal
,
mockUpdatedXBlockHtml
;
...
@@ -117,7 +140,7 @@ define(["jquery", "js/spec_helpers/create_sinon", "js/spec_helpers/edit_helpers"
...
@@ -117,7 +140,7 @@ define(["jquery", "js/spec_helpers/create_sinon", "js/spec_helpers/edit_helpers"
expect
(
editButtons
.
length
).
toBe
(
6
);
expect
(
editButtons
.
length
).
toBe
(
6
);
editButtons
.
first
().
click
();
editButtons
.
first
().
click
();
create_sinon
.
respondWithJson
(
requests
,
{
create_sinon
.
respondWithJson
(
requests
,
{
html
:
mockX
BlockEditorHtml
,
html
:
mockX
ModuleEditor
,
resources
:
[]
resources
:
[]
});
});
...
...
cms/static/js/spec/views/xblock_editor_spec.js
View file @
1b2d1858
...
@@ -49,20 +49,6 @@ define([ "jquery", "underscore", "js/spec_helpers/create_sinon", "js/spec_helper
...
@@ -49,20 +49,6 @@ define([ "jquery", "underscore", "js/spec_helpers/create_sinon", "js/spec_helper
expect
(
editor
.
$el
.
select
(
'.xblock-header'
)).
toBeTruthy
();
expect
(
editor
.
$el
.
select
(
'.xblock-header'
)).
toBeTruthy
();
expect
(
editor
.
getMode
()).
toEqual
(
'settings'
);
expect
(
editor
.
getMode
()).
toEqual
(
'settings'
);
});
});
it
(
'saves any custom metadata'
,
function
()
{
var
requests
=
create_sinon
.
requests
(
this
),
request
,
response
;
editor
.
render
();
create_sinon
.
respondWithJson
(
requests
,
{
html
:
mockXBlockEditorHtml
,
resources
:
[]
});
editor
.
save
();
request
=
requests
[
requests
.
length
-
1
];
response
=
JSON
.
parse
(
request
.
requestBody
);
expect
(
response
.
metadata
.
display_name
).
toBe
(
testDisplayName
);
expect
(
response
.
metadata
.
custom_field
).
toBe
(
'Custom Value'
);
});
});
});
describe
(
"Editing an xmodule"
,
function
()
{
describe
(
"Editing an xmodule"
,
function
()
{
...
...
cms/static/js/spec_helpers/edit_helpers.js
View file @
1b2d1858
/**
/**
* Provides helper methods for invoking Studio editors in Jasmine tests.
* Provides helper methods for invoking Studio editors in Jasmine tests.
*/
*/
define
([
"jquery"
,
"
js/spec_helpers/create_sinon"
,
"js/spec_helpers/modal_helpers"
,
"js/views/modals/edit_xblock
"
,
define
([
"jquery"
,
"
underscore"
,
"js/spec_helpers/create_sinon"
,
"js/spec_helpers/modal_helpers
"
,
"xmodule"
,
"coffee/src/main"
,
"xblock/cms.runtime.v1"
],
"
js/views/modals/edit_xblock"
,
"
xmodule"
,
"coffee/src/main"
,
"xblock/cms.runtime.v1"
],
function
(
$
,
create_sinon
,
modal_helpers
,
EditXBlockModal
)
{
function
(
$
,
_
,
create_sinon
,
modal_helpers
,
EditXBlockModal
)
{
var
editorTemplate
=
readFixtures
(
'metadata-editor.underscore'
),
var
editorTemplate
=
readFixtures
(
'metadata-editor.underscore'
),
numberEntryTemplate
=
readFixtures
(
'metadata-number-entry.underscore'
),
numberEntryTemplate
=
readFixtures
(
'metadata-number-entry.underscore'
),
...
@@ -12,19 +12,15 @@ define(["jquery", "js/spec_helpers/create_sinon", "js/spec_helpers/modal_helpers
...
@@ -12,19 +12,15 @@ define(["jquery", "js/spec_helpers/create_sinon", "js/spec_helpers/modal_helpers
editorModeButtonTemplate
=
readFixtures
(
'editor-mode-button.underscore'
),
editorModeButtonTemplate
=
readFixtures
(
'editor-mode-button.underscore'
),
installMockXBlock
,
installMockXBlock
,
uninstallMockXBlock
,
uninstallMockXBlock
,
hasSavedMockXBlock
,
installMockXModule
,
installMockXModule
,
uninstallMockXModule
,
uninstallMockXModule
,
hasSavedMockXModule
,
installEditTemplates
,
installEditTemplates
,
showEditModal
;
showEditModal
;
installMockXBlock
=
function
(
mockResult
)
{
installMockXBlock
=
function
(
mockResult
)
{
window
.
MockXBlock
=
function
(
runtime
,
element
)
{
window
.
MockXBlock
=
function
(
runtime
,
element
)
{
return
{
return
{
save
:
function
()
{
runtime
:
runtime
return
mockResult
;
}
};
};
};
};
};
};
...
@@ -58,9 +54,9 @@ define(["jquery", "js/spec_helpers/create_sinon", "js/spec_helpers/modal_helpers
...
@@ -58,9 +54,9 @@ define(["jquery", "js/spec_helpers/create_sinon", "js/spec_helpers/modal_helpers
appendSetFixtures
(
$
(
"<script>"
,
{
id
:
"metadata-string-entry"
,
type
:
"text/template"
}).
text
(
stringEntryTemplate
));
appendSetFixtures
(
$
(
"<script>"
,
{
id
:
"metadata-string-entry"
,
type
:
"text/template"
}).
text
(
stringEntryTemplate
));
};
};
showEditModal
=
function
(
requests
,
xblockElement
,
model
,
mockHtml
)
{
showEditModal
=
function
(
requests
,
xblockElement
,
model
,
mockHtml
,
options
)
{
var
modal
=
new
EditXBlockModal
({});
var
modal
=
new
EditXBlockModal
({});
modal
.
edit
(
xblockElement
,
model
);
modal
.
edit
(
xblockElement
,
model
,
options
);
create_sinon
.
respondWithJson
(
requests
,
{
create_sinon
.
respondWithJson
(
requests
,
{
html
:
mockHtml
,
html
:
mockHtml
,
"resources"
:
[]
"resources"
:
[]
...
...
cms/static/js/spec_helpers/modal_helpers.js
View file @
1b2d1858
...
@@ -47,7 +47,7 @@ define(["jquery"],
...
@@ -47,7 +47,7 @@ define(["jquery"],
cancelModal
=
function
(
modal
)
{
cancelModal
=
function
(
modal
)
{
var
modalElement
,
cancelButton
;
var
modalElement
,
cancelButton
;
modalElement
=
getModalElement
(
modal
);
modalElement
=
getModalElement
(
modal
);
cancelButton
=
modalElement
.
find
(
'.action-cancel'
);
cancelButton
=
modalElement
.
find
(
'.action-cancel
:visible
'
);
expect
(
cancelButton
.
length
).
toBe
(
1
);
expect
(
cancelButton
.
length
).
toBe
(
1
);
cancelButton
.
click
();
cancelButton
.
click
();
};
};
...
...
cms/static/js/views/modals/base_modal.js
View file @
1b2d1858
...
@@ -71,8 +71,10 @@ define(["jquery", "underscore", "gettext", "js/views/baseview"],
...
@@ -71,8 +71,10 @@ define(["jquery", "underscore", "gettext", "js/views/baseview"],
},
},
cancel
:
function
(
event
)
{
cancel
:
function
(
event
)
{
event
.
preventDefault
();
if
(
event
)
{
event
.
stopPropagation
();
// Make sure parent modals don't see the click
event
.
preventDefault
();
event
.
stopPropagation
();
// Make sure parent modals don't see the click
}
this
.
hide
();
this
.
hide
();
},
},
...
@@ -98,7 +100,21 @@ define(["jquery", "underscore", "gettext", "js/views/baseview"],
...
@@ -98,7 +100,21 @@ define(["jquery", "underscore", "gettext", "js/views/baseview"],
name
:
name
,
name
:
name
,
isPrimary
:
isPrimary
isPrimary
:
isPrimary
});
});
this
.
$
(
'.modal-actions ul'
).
append
(
html
);
this
.
getActionBar
().
find
(
'ul'
).
append
(
html
);
},
/**
* Returns the action bar that contains the modal's action buttons.
*/
getActionBar
:
function
()
{
return
this
.
$
(
'.modal-window > div > .modal-actions'
);
},
/**
* Returns the action button of the specified type.
*/
getActionButton
:
function
(
type
)
{
return
this
.
getActionBar
().
find
(
'.action-'
+
type
);
},
},
resize
:
function
()
{
resize
:
function
()
{
...
...
cms/static/js/views/modals/edit_xblock.js
View file @
1b2d1858
...
@@ -37,6 +37,10 @@ define(["jquery", "underscore", "gettext", "js/views/modals/base_modal",
...
@@ -37,6 +37,10 @@ define(["jquery", "underscore", "gettext", "js/views/modals/base_modal",
this
.
editOptions
=
options
;
this
.
editOptions
=
options
;
this
.
render
();
this
.
render
();
this
.
show
();
this
.
show
();
// Hide the action bar until we know which buttons we want
this
.
getActionBar
().
hide
();
// Display the xblock after the modal is shown as there are some xblocks
// Display the xblock after the modal is shown as there are some xblocks
// that depend upon being visible when they initialize, e.g. the problem xmodule.
// that depend upon being visible when they initialize, e.g. the problem xmodule.
this
.
displayXBlock
();
this
.
displayXBlock
();
...
@@ -60,7 +64,17 @@ define(["jquery", "underscore", "gettext", "js/views/modals/base_modal",
...
@@ -60,7 +64,17 @@ define(["jquery", "underscore", "gettext", "js/views/modals/base_modal",
onDisplayXBlock
:
function
()
{
onDisplayXBlock
:
function
()
{
var
editorView
=
this
.
editorView
,
var
editorView
=
this
.
editorView
,
title
=
this
.
getTitle
();
title
=
this
.
getTitle
(),
xblock
=
editorView
.
xblock
,
runtime
=
xblock
.
runtime
;
// Notify the runtime that the modal has been shown
if
(
runtime
)
{
this
.
runtime
=
runtime
;
runtime
.
notify
(
"edit-modal-shown"
,
this
);
}
// Update the modal's header
if
(
editorView
.
hasCustomTabs
())
{
if
(
editorView
.
hasCustomTabs
())
{
// Hide the modal's header as the custom editor provides its own
// Hide the modal's header as the custom editor provides its own
this
.
$
(
'.modal-header'
).
hide
();
this
.
$
(
'.modal-header'
).
hide
();
...
@@ -74,9 +88,28 @@ define(["jquery", "underscore", "gettext", "js/views/modals/base_modal",
...
@@ -74,9 +88,28 @@ define(["jquery", "underscore", "gettext", "js/views/modals/base_modal",
this
.
selectMode
(
editorView
.
mode
);
this
.
selectMode
(
editorView
.
mode
);
}
}
}
}
// If the xblock is not using custom buttons then choose which buttons to show
if
(
!
editorView
.
hasCustomButtons
())
{
// If the xblock does not support save then disable the save button
if
(
!
xblock
.
save
)
{
this
.
disableSave
();
}
this
.
getActionBar
().
show
();
}
// Resize the modal to fit the window
this
.
resize
();
this
.
resize
();
},
},
disableSave
:
function
()
{
var
saveButton
=
this
.
getActionButton
(
'save'
),
cancelButton
=
this
.
getActionButton
(
'cancel'
);
saveButton
.
hide
();
cancelButton
.
text
(
gettext
(
'OK'
));
cancelButton
.
addClass
(
'action-primary'
);
},
getTitle
:
function
()
{
getTitle
:
function
()
{
var
displayName
=
this
.
xblockElement
.
find
(
'.xblock-header .header-details'
).
text
().
trim
();
var
displayName
=
this
.
xblockElement
.
find
(
'.xblock-header .header-details'
).
text
().
trim
();
// If not found, try the old unit page style rendering
// If not found, try the old unit page style rendering
...
@@ -117,23 +150,28 @@ define(["jquery", "underscore", "gettext", "js/views/modals/base_modal",
...
@@ -117,23 +150,28 @@ define(["jquery", "underscore", "gettext", "js/views/modals/base_modal",
},
},
save
:
function
(
event
)
{
save
:
function
(
event
)
{
var
self
=
this
,
xblockInfo
=
this
.
xblockInfo
,
refresh
=
self
.
editOptions
.
refresh
;
event
.
preventDefault
();
event
.
preventDefault
();
this
.
editorView
.
save
({
this
.
editorView
.
save
({
success
:
function
()
{
success
:
_
.
bind
(
this
.
onSave
,
this
)
self
.
hide
();
if
(
refresh
)
{
refresh
(
xblockInfo
);
}
}
});
});
},
},
onSave
:
function
()
{
var
refresh
=
this
.
editOptions
.
refresh
;
this
.
hide
();
if
(
refresh
)
{
refresh
(
this
.
xblockInfo
);
}
},
hide
:
function
()
{
hide
:
function
()
{
BaseModal
.
prototype
.
hide
.
call
(
this
);
BaseModal
.
prototype
.
hide
.
call
(
this
);
// Notify the runtime that the modal has been hidden
if
(
this
.
runtime
)
{
this
.
runtime
.
notify
(
'edit-modal-hidden'
);
}
// Completely clear the contents of the modal
// Completely clear the contents of the modal
this
.
undelegateEvents
();
this
.
undelegateEvents
();
this
.
$el
.
html
(
""
);
this
.
$el
.
html
(
""
);
...
...
cms/static/js/views/xblock_editor.js
View file @
1b2d1858
...
@@ -49,6 +49,10 @@ define(["jquery", "underscore", "gettext", "js/views/feedback_notification", "js
...
@@ -49,6 +49,10 @@ define(["jquery", "underscore", "gettext", "js/views/feedback_notification", "js
return
this
.
$
(
'.editor-with-tabs'
).
length
>
0
;
return
this
.
$
(
'.editor-with-tabs'
).
length
>
0
;
},
},
hasCustomButtons
:
function
()
{
return
this
.
$
(
'.editor-with-buttons'
).
length
>
0
;
},
createMetadataEditor
:
function
()
{
createMetadataEditor
:
function
()
{
var
metadataEditor
,
var
metadataEditor
,
metadataData
,
metadataData
,
...
@@ -88,27 +92,36 @@ define(["jquery", "underscore", "gettext", "js/views/feedback_notification", "js
...
@@ -88,27 +92,36 @@ define(["jquery", "underscore", "gettext", "js/views/feedback_notification", "js
var
xblockInfo
=
this
.
model
,
var
xblockInfo
=
this
.
model
,
data
,
data
,
saving
;
saving
;
data
=
this
.
getXBlockData
();
data
=
this
.
getXModuleData
();
saving
=
new
NotificationView
.
Mini
({
if
(
data
)
{
title
:
gettext
(
'Saving…'
)
saving
=
new
NotificationView
.
Mini
({
});
title
:
gettext
(
'Saving…'
)
saving
.
show
();
});
return
xblockInfo
.
save
(
data
).
done
(
function
()
{
saving
.
show
();
var
success
=
options
.
success
;
return
xblockInfo
.
save
(
data
).
done
(
function
()
{
saving
.
hide
();
var
success
=
options
.
success
;
if
(
success
)
{
saving
.
hide
();
success
();
if
(
success
)
{
}
success
();
});
}
});
}
},
},
getXBlockData
:
function
()
{
/**
* Returns the data saved for the xmodule. Note that this *does not* work for XBlocks.
*/
getXModuleData
:
function
()
{
var
xblock
=
this
.
xblock
,
var
xblock
=
this
.
xblock
,
metadataEditor
=
this
.
getMetadataEditor
(),
metadataEditor
=
this
.
getMetadataEditor
(),
data
;
data
=
null
;
data
=
xblock
.
save
();
if
(
xblock
.
save
)
{
if
(
metadataEditor
)
{
data
=
xblock
.
save
();
data
.
metadata
=
_
.
extend
(
data
.
metadata
||
{},
this
.
getChangedMetadata
());
if
(
metadataEditor
)
{
data
.
metadata
=
_
.
extend
(
data
.
metadata
||
{},
this
.
getChangedMetadata
());
}
}
else
{
console
.
error
(
'Cannot save xblock as it has no save method'
);
}
}
return
data
;
return
data
;
},
},
...
...
cms/static/sass/elements/_modal-window.scss
View file @
1b2d1858
...
@@ -18,7 +18,6 @@
...
@@ -18,7 +18,6 @@
.modal-content
{
.modal-content
{
position
:
relative
;
position
:
relative
;
box-shadow
:
0
0
3px
$shadow-d1
;
background-color
:
$white
;
background-color
:
$white
;
padding
:
5%
;
padding
:
5%
;
}
}
...
@@ -52,7 +51,9 @@
...
@@ -52,7 +51,9 @@
}
}
}
}
// TODO: need to sync up (alongside general editing mode) with xblocks.scss UI
.modal-chin
,
.modal-chin
,
.xblock-actions
,
.modal-actions
{
.modal-actions
{
padding
:
(
$baseline
*
0
.75
)
2%
(
$baseline
/
2
)
2%
;
padding
:
(
$baseline
*
0
.75
)
2%
(
$baseline
/
2
)
2%
;
...
@@ -190,6 +191,22 @@
...
@@ -190,6 +191,22 @@
}
}
}
}
// xblock custom actions
.modal-window
.editor-with-buttons
{
margin-bottom
:
(
$baseline
*
3
);
// TODO: need to sync up (alongside general editing mode) with xblocks.scss UI
.xblock-actions
{
background-color
:
$gray-l4
;
position
:
absolute
;
width
:
100%
;
bottom
:
0
;
}
}
// special overrides for video module editor/hidden tab editors
// special overrides for video module editor/hidden tab editors
.modal-lg.modal-type-video
{
.modal-lg.modal-type-video
{
...
...
cms/templates/component.html
View file @
1b2d1858
<
%!
from
django
.
utils
.
translation
import
ugettext
as
_
%
>
<
%!
from
django
.
utils
.
translation
import
ugettext
as
_
%
>
<
%
namespace
name=
'static'
file=
'static_content.html'
/>
<
%
namespace
name=
'static'
file=
'static_content.html'
/>
<div
class=
"wrapper wrapper-component-editor"
>
<div
class=
"component-editor"
>
<div
class=
"component-edit-header"
>
<span
class=
"component-name"
></span>
<ul
class=
"nav-edit-modes"
>
<li
id=
"editor-mode"
class=
"mode active-mode"
aria-controls=
"editor-tab"
role=
"tab"
>
<a
href=
"#"
>
${_("Editor")}
</a>
</li>
<li
id=
"settings-mode"
class=
"mode active-mode"
aria-controls=
"settings-tab"
role=
"tab"
>
<a
href=
"#"
>
${_("Settings")}
</a>
</li>
</ul>
</div>
<!-- Editor Header -->
<div
class=
"component-edit-modes"
>
<div
class=
"module-editor"
/>
</div>
## Native XBlocks render their own save/cancel buttons
% if show_save_cancel:
<div
class=
"row module-actions"
>
<a
href=
"#"
class=
"save-button action-primary action"
>
${_("Save")}
</a>
<a
href=
"#"
class=
"cancel-button action-secondary action"
>
${_("Cancel")}
</a>
</div>
<!-- Module Actions-->
% endif
</div>
</div>
<div
class=
"wrapper wrapper-component-action-header"
>
<div
class=
"wrapper wrapper-component-action-header"
>
<div
class=
"component-header"
>
<div
class=
"component-header"
>
${label}
${label}
...
...
cms/templates/container.html
View file @
1b2d1858
...
@@ -130,7 +130,4 @@ main_xblock_info = {
...
@@ -130,7 +130,4 @@ main_xblock_info = {
</section>
</section>
</div>
</div>
</div>
</div>
<div
class=
"edit-xblock-modal"
></div>
</
%
block>
</
%
block>
cms/templates/edit-tabs.html
View file @
1b2d1858
...
@@ -178,6 +178,4 @@
...
@@ -178,6 +178,4 @@
<span
class=
"label"
>
${_("close modal")}
</span>
<span
class=
"label"
>
${_("close modal")}
</span>
</a>
</a>
</div>
</div>
<div
class=
"edit-xblock-modal"
></div>
</
%
block>
</
%
block>
cms/templates/js/basic-modal.underscore
View file @
1b2d1858
...
@@ -5,7 +5,7 @@
...
@@ -5,7 +5,7 @@
role="dialog">
role="dialog">
<div class="modal-window-overlay"></div>
<div class="modal-window-overlay"></div>
<div class="modal-window confirm modal-editor modal-<%= size %> modal-type-<%= type %>">
<div class="modal-window confirm modal-editor modal-<%= size %> modal-type-<%= type %>">
<div class="<%= name %>-modal"
action="#"
>
<div class="<%= name %>-modal">
<div class="modal-header">
<div class="modal-header">
<h2 class="title modal-window-title"><%= title %></h2>
<h2 class="title modal-window-title"><%= title %></h2>
<ul class="editor-modes action-list action-modes">
<ul class="editor-modes action-list action-modes">
...
...
cms/templates/js/mock/mock-xblock-editor-with-custom-buttons.underscore
0 → 100644
View file @
1b2d1858
<div class="xblock xblock-studio_view" data-runtime-version="1" data-usage-id="i4x:;_;_edX;_mock"
data-init="MockXBlock" data-runtime-class="StudioRuntime" data-block-type="mock" tabindex="0">
<div class="mock-xblock editor-with-buttons">
<h3>Mock XBlock Editor</h3>
<div class="modal-actions">
<h3 class="sr">Actions</h3>
<ul>
<li class="action-item">
<a href="#" class="button action-primary action-save">Save</a>
</li>
<li class="action-item">
<a href="#" class="button action-cancel">Cancel</a>
</li>
</ul>
</div>
</div>
</div>
cms/templates/unit.html
View file @
1b2d1858
...
@@ -225,5 +225,4 @@ require(["domReady!", "jquery", "js/models/module_info", "coffee/src/views/unit"
...
@@ -225,5 +225,4 @@ require(["domReady!", "jquery", "js/models/module_info", "coffee/src/views/unit"
</div>
</div>
</div>
</div>
</div>
</div>
<div
class=
"edit-xblock-modal"
></div>
</
%
block>
</
%
block>
common/static/coffee/src/xblock/core.coffee
View file @
1b2d1858
...
@@ -12,6 +12,7 @@
...
@@ -12,6 +12,7 @@
runtime
=
new
window
[
runtime
][
"v
#{
version
}
"
]
runtime
=
new
window
[
runtime
][
"v
#{
version
}
"
]
initFn
=
window
[
initFnName
]
initFn
=
window
[
initFnName
]
block
=
initFn
(
runtime
,
element
)
?
{}
block
=
initFn
(
runtime
,
element
)
?
{}
block
.
runtime
=
runtime
else
else
elementTag
=
$
(
'<div>'
).
append
(
$element
.
clone
()).
html
();
elementTag
=
$
(
'<div>'
).
append
(
$element
.
clone
()).
html
();
console
.
log
(
"Block
#{
elementTag
}
is missing data-runtime, data-runtime-version or data-init, and can't be initialized"
)
console
.
log
(
"Block
#{
elementTag
}
is missing data-runtime, data-runtime-version or data-init, and can't be initialized"
)
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment