Commit 2dfca626 by Marco Morales

Merge pull request #8151 from edx/marco/capa-styling

Updated styling for basic capa problems (multiple choice, dropdown, checkboxes, numerical input and text input) including support for mobile app experience. 
parents 13dbfb86 aa5e2f49
...@@ -351,7 +351,7 @@ ...@@ -351,7 +351,7 @@
</p> </p>
<p>What Apple device competed with the portable CD player?</p> <p>What Apple device competed with the portable CD player?</p>
<span><form class="choicegroup capa_inputtype" id="inputtype_i4x-AndyA-ABT101-problem-46d2b65d793549e2876729d55df9a2cb_2_1"> <span><form class="choicegroup capa_inputtype" id="inputtype_i4x-AndyA-ABT101-problem-46d2b65d793549e2876729d55df9a2cb_2_1">
<div class="indicator_container"> <div class="indicator-container">
<span class="unanswered" style="display:inline-block;" id="status_i4x-AndyA-ABT101-problem-46d2b65d793549e2876729d55df9a2cb_2_1"></span> <span class="unanswered" style="display:inline-block;" id="status_i4x-AndyA-ABT101-problem-46d2b65d793549e2876729d55df9a2cb_2_1"></span>
</div> </div>
......
...@@ -69,7 +69,7 @@ registry = TagRegistry() # pylint: disable=invalid-name ...@@ -69,7 +69,7 @@ registry = TagRegistry() # pylint: disable=invalid-name
class Status(object): class Status(object):
""" """
Problem status Problem status
attributes: classname, display_name attributes: classname, display_name, display_tooltip
""" """
css_classes = { css_classes = {
# status: css class # status: css class
...@@ -77,7 +77,7 @@ class Status(object): ...@@ -77,7 +77,7 @@ class Status(object):
'incomplete': 'incorrect', 'incomplete': 'incorrect',
'queued': 'processing', 'queued': 'processing',
} }
__slots__ = ('classname', '_status', 'display_name') __slots__ = ('classname', '_status', 'display_name', 'display_tooltip')
def __init__(self, status, gettext_func=unicode): def __init__(self, status, gettext_func=unicode):
self.classname = self.css_classes.get(status, status) self.classname = self.css_classes.get(status, status)
...@@ -90,7 +90,16 @@ class Status(object): ...@@ -90,7 +90,16 @@ class Status(object):
'unsubmitted': _('unanswered'), 'unsubmitted': _('unanswered'),
'queued': _('processing'), 'queued': _('processing'),
} }
tooltips = {
# Translators: these are tooltips that indicate the state of an assessment question
'correct': _('This is correct.'),
'incorrect': _('This is incorrect.'),
'unanswered': _('This is unanswered.'),
'unsubmitted': _('This is unanswered.'),
'queued': _('This is being processed.'),
}
self.display_name = names.get(status, unicode(status)) self.display_name = names.get(status, unicode(status))
self.display_tooltip = tooltips.get(status, u'')
self._status = status or '' self._status = status or ''
def __str__(self): def __str__(self):
......
<form class="choicegroup capa_inputtype" id="inputtype_${id}"> <form class="choicegroup capa_inputtype" id="inputtype_${id}">
<div class="indicator_container">
% if input_type == 'checkbox' or not value:
<span class="status ${status.classname if show_correctness != 'never' else 'unanswered'}"
id="status_${id}"
aria-describedby="inputtype_${id}">
<span class="sr">
%for choice_id, choice_description in choices:
% if choice_id in value:
${choice_description},
%endif
%endfor
-
${status.display_name}
</span>
</span>
% endif
</div>
<fieldset role="${input_type}group" aria-label="${label}"> <fieldset role="${input_type}group" aria-label="${label}">
% for choice_id, choice_description in choices: % for choice_id, choice_description in choices:
<label for="input_${id}_${choice_id}" <label for="input_${id}_${choice_id}"
## If the student has selected this choice... ## If the student has selected this choice...
...@@ -58,7 +39,21 @@ ...@@ -58,7 +39,21 @@
% endfor % endfor
<span id="answer_${id}"></span> <span id="answer_${id}"></span>
</fieldset> </fieldset>
<div class="indicator-container">
% if input_type == 'checkbox' or not value:
<span class="status ${status.classname if show_correctness != 'never' else 'unanswered'}" id="status_${id}" aria-describedby="inputtype_${id}" data-tooltip="${status.display_tooltip}">
<span class="sr">
%for choice_id, choice_description in choices:
% if choice_id in value:
${choice_description},
%endif
%endfor
-
${status.display_name}
</span>
</span>
% endif
</div>
% if show_correctness == "never" and (value or status not in ['unsubmitted']): % if show_correctness == "never" and (value or status not in ['unsubmitted']):
<div class="capa_alert">${submitted_message}</div> <div class="capa_alert">${submitted_message}</div>
%endif %endif
......
...@@ -9,12 +9,7 @@ ...@@ -9,12 +9,7 @@
<section id="choicetextinput_${id}" class="choicetextinput"> <section id="choicetextinput_${id}" class="choicetextinput">
<form class="choicetextgroup capa_inputtype" id="inputtype_${id}"> <form class="choicetextgroup capa_inputtype" id="inputtype_${id}">
<div class="script_placeholder" data-src="${STATIC_URL}js/capa/choicetextinput.js"/> <div class="script_placeholder" data-src="${STATIC_URL}js/capa/choicetextinput.js"/>
<div class="indicator_container">
% if input_type == 'checkbox' or not element_checked:
<span class="status ${status.classname}" id="status_${id}"></span>
% endif
</div>
<fieldset aria-label="${label}"> <fieldset aria-label="${label}">
% for choice_id, choice_description in choices: % for choice_id, choice_description in choices:
<%choice_id= choice_id %> <%choice_id= choice_id %>
...@@ -62,6 +57,13 @@ ...@@ -62,6 +57,13 @@
<span id="answer_${id}"></span> <span id="answer_${id}"></span>
</fieldset> </fieldset>
<input class= "choicetextvalue" type="hidden" name="input_${id}{}" id="input_${id}" value="${value|h}" /> <input class= "choicetextvalue" type="hidden" name="input_${id}{}" id="input_${id}" value="${value|h}" />
<div class="indicator-container">
% if input_type == 'checkbox' or not element_checked:
<span class="status ${status.classname}" id="status_${id}"></span>
% endif
</div>
% if show_correctness == "never" and (value or status not in ['unsubmitted']): % if show_correctness == "never" and (value or status not in ['unsubmitted']):
<div class="capa_alert">${_(submitted_message)}</div> <div class="capa_alert">${_(submitted_message)}</div>
%endif %endif
......
...@@ -10,9 +10,11 @@ ...@@ -10,9 +10,11 @@
% endif % endif
/> />
<p class="status" id="${id}_status"> <span class="status" id="${id}_status" data-tooltip="${status.display_tooltip}">
${status.display_name} <span class="sr">
</p> ${status.display_name}
</span>
</span>
<div id="input_${id}_preview" class="equation"> <div id="input_${id}_preview" class="equation">
\[\] \[\]
......
...@@ -13,12 +13,13 @@ ...@@ -13,12 +13,13 @@
</select> </select>
<span id="answer_${id}"></span> <span id="answer_${id}"></span>
<span class="status ${status.classname}" <div class="indicator-container">
id="status_${id}" <span class="status ${status.classname}"
aria-describedby="input_${id}"> id="status_${id}"
<span class="sr">${value|h} - ${status.display_name}</span> aria-describedby="input_${id}" data-tooltip="${status.display_tooltip}">
</span> <span class="sr">${value|h} - ${status.display_name}</span>
</span>
</div>
% if msg: % if msg:
<span class="message">${msg|n}</span> <span class="message">${msg|n}</span>
% endif % endif
......
...@@ -27,18 +27,20 @@ ...@@ -27,18 +27,20 @@
/> />
${trailing_text | h} ${trailing_text | h}
<p class="status" <span class="status"
%if status != 'unsubmitted': %if status != 'unsubmitted':
%endif %endif
aria-describedby="input_${id}"> aria-describedby="input_${id}" data-tooltip="${status.display_tooltip}">
%if value: <span class="sr">
${value|h} %if value:
% else: ${value|h}
${label} % else:
%endif ${label}
- %endif
${status.display_name} -
</p> ${status.display_name}
</span>
</span>
<p id="answer_${id}" class="answer"></p> <p id="answer_${id}" class="answer"></p>
......
...@@ -144,7 +144,7 @@ class ChoiceGroupTemplateTest(TemplateTestCase): ...@@ -144,7 +144,7 @@ class ChoiceGroupTemplateTest(TemplateTestCase):
# Should mark the entire problem correct # Should mark the entire problem correct
xml = self.render_to_xml(self.context) xml = self.render_to_xml(self.context)
xpath = "//div[@class='indicator_container']/span[@class='status correct']" xpath = "//div[@class='indicator-container']/span[@class='status correct']"
self.assert_has_xpath(xml, xpath, self.context) self.assert_has_xpath(xml, xpath, self.context)
# Should NOT mark individual options # Should NOT mark individual options
...@@ -172,7 +172,7 @@ class ChoiceGroupTemplateTest(TemplateTestCase): ...@@ -172,7 +172,7 @@ class ChoiceGroupTemplateTest(TemplateTestCase):
for test_conditions in conditions: for test_conditions in conditions:
self.context.update(test_conditions) self.context.update(test_conditions)
xml = self.render_to_xml(self.context) xml = self.render_to_xml(self.context)
xpath = "//div[@class='indicator_container']/span[@class='status incorrect']" xpath = "//div[@class='indicator-container']/span[@class='status incorrect']"
self.assert_has_xpath(xml, xpath, self.context) self.assert_has_xpath(xml, xpath, self.context)
# Should NOT mark individual options # Should NOT mark individual options
...@@ -204,7 +204,7 @@ class ChoiceGroupTemplateTest(TemplateTestCase): ...@@ -204,7 +204,7 @@ class ChoiceGroupTemplateTest(TemplateTestCase):
for test_conditions in conditions: for test_conditions in conditions:
self.context.update(test_conditions) self.context.update(test_conditions)
xml = self.render_to_xml(self.context) xml = self.render_to_xml(self.context)
xpath = "//div[@class='indicator_container']/span[@class='status unanswered']" xpath = "//div[@class='indicator-container']/span[@class='status unanswered']"
self.assert_has_xpath(xml, xpath, self.context) self.assert_has_xpath(xml, xpath, self.context)
# Should NOT mark individual options # Should NOT mark individual options
...@@ -234,7 +234,7 @@ class ChoiceGroupTemplateTest(TemplateTestCase): ...@@ -234,7 +234,7 @@ class ChoiceGroupTemplateTest(TemplateTestCase):
self.assert_has_xpath(xml, xpath, self.context) self.assert_has_xpath(xml, xpath, self.context)
# Should NOT mark the whole problem # Should NOT mark the whole problem
xpath = "//div[@class='indicator_container']/span" xpath = "//div[@class='indicator-container']/span"
self.assert_no_xpath(xml, xpath, self.context) self.assert_no_xpath(xml, xpath, self.context)
def test_option_marked_incorrect(self): def test_option_marked_incorrect(self):
...@@ -255,7 +255,7 @@ class ChoiceGroupTemplateTest(TemplateTestCase): ...@@ -255,7 +255,7 @@ class ChoiceGroupTemplateTest(TemplateTestCase):
self.assert_has_xpath(xml, xpath, self.context) self.assert_has_xpath(xml, xpath, self.context)
# Should NOT mark the whole problem # Should NOT mark the whole problem
xpath = "//div[@class='indicator_container']/span" xpath = "//div[@class='indicator-container']/span"
self.assert_no_xpath(xml, xpath, self.context) self.assert_no_xpath(xml, xpath, self.context)
def test_never_show_correctness(self): def test_never_show_correctness(self):
...@@ -289,10 +289,10 @@ class ChoiceGroupTemplateTest(TemplateTestCase): ...@@ -289,10 +289,10 @@ class ChoiceGroupTemplateTest(TemplateTestCase):
xml = self.render_to_xml(self.context) xml = self.render_to_xml(self.context)
# Should NOT mark the entire problem correct/incorrect # Should NOT mark the entire problem correct/incorrect
xpath = "//div[@class='indicator_container']/span[@class='status correct']" xpath = "//div[@class='indicator-container']/span[@class='status correct']"
self.assert_no_xpath(xml, xpath, self.context) self.assert_no_xpath(xml, xpath, self.context)
xpath = "//div[@class='indicator_container']/span[@class='status incorrect']" xpath = "//div[@class='indicator-container']/span[@class='status incorrect']"
self.assert_no_xpath(xml, xpath, self.context) self.assert_no_xpath(xml, xpath, self.context)
# Should NOT mark individual options # Should NOT mark individual options
...@@ -388,9 +388,9 @@ class TextlineTemplateTest(TemplateTestCase): ...@@ -388,9 +388,9 @@ class TextlineTemplateTest(TemplateTestCase):
xpath = "//div[@class='%s ']" % div_class xpath = "//div[@class='%s ']" % div_class
self.assert_has_xpath(xml, xpath, self.context) self.assert_has_xpath(xml, xpath, self.context)
# Expect that we get a <p> with class="status" # Expect that we get a <span> with class="status"
# (used to by CSS to draw the green check / red x) # (used to by CSS to draw the green check / red x)
self.assert_has_text(xml, "//p[@class='status']", self.assert_has_text(xml, "//span[@class='status']/span[@class='sr']",
status_mark, exact=False) status_mark, exact=False)
def test_label(self): def test_label(self):
...@@ -848,7 +848,7 @@ class ChoiceTextGroupTemplateTest(TemplateTestCase): ...@@ -848,7 +848,7 @@ class ChoiceTextGroupTemplateTest(TemplateTestCase):
# Should mark the entire problem correct # Should mark the entire problem correct
xml = self.render_to_xml(self.context) xml = self.render_to_xml(self.context)
xpath = "//div[@class='indicator_container']/span[@class='status correct']" xpath = "//div[@class='indicator-container']/span[@class='status correct']"
self.assert_has_xpath(xml, xpath, self.context) self.assert_has_xpath(xml, xpath, self.context)
# Should NOT mark individual options # Should NOT mark individual options
...@@ -875,7 +875,7 @@ class ChoiceTextGroupTemplateTest(TemplateTestCase): ...@@ -875,7 +875,7 @@ class ChoiceTextGroupTemplateTest(TemplateTestCase):
for test_conditions in conditions: for test_conditions in conditions:
self.context.update(test_conditions) self.context.update(test_conditions)
xml = self.render_to_xml(self.context) xml = self.render_to_xml(self.context)
xpath = "//div[@class='indicator_container']/span[@class='status incorrect']" xpath = "//div[@class='indicator-container']/span[@class='status incorrect']"
self.assert_has_xpath(xml, xpath, self.context) self.assert_has_xpath(xml, xpath, self.context)
# Should NOT mark individual options # Should NOT mark individual options
...@@ -907,7 +907,7 @@ class ChoiceTextGroupTemplateTest(TemplateTestCase): ...@@ -907,7 +907,7 @@ class ChoiceTextGroupTemplateTest(TemplateTestCase):
for test_conditions in conditions: for test_conditions in conditions:
self.context.update(test_conditions) self.context.update(test_conditions)
xml = self.render_to_xml(self.context) xml = self.render_to_xml(self.context)
xpath = "//div[@class='indicator_container']/span[@class='status unanswered']" xpath = "//div[@class='indicator-container']/span[@class='status unanswered']"
self.assert_has_xpath(xml, xpath, self.context) self.assert_has_xpath(xml, xpath, self.context)
# Should NOT mark individual options # Should NOT mark individual options
...@@ -937,7 +937,7 @@ class ChoiceTextGroupTemplateTest(TemplateTestCase): ...@@ -937,7 +937,7 @@ class ChoiceTextGroupTemplateTest(TemplateTestCase):
self.assert_has_xpath(xml, xpath, self.context) self.assert_has_xpath(xml, xpath, self.context)
# Should NOT mark the whole problem # Should NOT mark the whole problem
xpath = "//div[@class='indicator_container']/span" xpath = "//div[@class='indicator-container']/span"
self.assert_no_xpath(xml, xpath, self.context) self.assert_no_xpath(xml, xpath, self.context)
def test_option_marked_incorrect(self): def test_option_marked_incorrect(self):
...@@ -957,7 +957,7 @@ class ChoiceTextGroupTemplateTest(TemplateTestCase): ...@@ -957,7 +957,7 @@ class ChoiceTextGroupTemplateTest(TemplateTestCase):
self.assert_has_xpath(xml, xpath, self.context) self.assert_has_xpath(xml, xpath, self.context)
# Should NOT mark the whole problem # Should NOT mark the whole problem
xpath = "//div[@class='indicator_container']/span" xpath = "//div[@class='indicator-container']/span"
self.assert_no_xpath(xml, xpath, self.context) self.assert_no_xpath(xml, xpath, self.context)
def test_label(self): def test_label(self):
......
...@@ -323,7 +323,7 @@ describe 'Problem', -> ...@@ -323,7 +323,7 @@ describe 'Problem', ->
<div><p></p><span><section id="choicetextinput_1_2_1" class="choicetextinput"> <div><p></p><span><section id="choicetextinput_1_2_1" class="choicetextinput">
<form class="choicetextgroup capa_inputtype" id="inputtype_1_2_1"> <form class="choicetextgroup capa_inputtype" id="inputtype_1_2_1">
<div class="indicator_container"> <div class="indicator-container">
<span class="unanswered" style="display:inline-block;" id="status_1_2_1"></span> <span class="unanswered" style="display:inline-block;" id="status_1_2_1"></span>
</div> </div>
<fieldset> <fieldset>
......
...@@ -468,9 +468,9 @@ class @Problem ...@@ -468,9 +468,9 @@ class @Problem
# They should set handlers on each <input> to reset the whole. # They should set handlers on each <input> to reset the whole.
formulaequationinput: (element) -> formulaequationinput: (element) ->
$(element).find('input').on 'input', -> $(element).find('input').on 'input', ->
$p = $(element).find('p.status') $p = $(element).find('span.status')
`// Translators: the word unanswered here is about answering a problem the student must solve.` `// Translators: the word unanswered here is about answering a problem the student must solve.`
$p.parent().removeClass().addClass "unanswered" $p.parent().removeClass().addClass "unsubmitted"
choicegroup: (element) -> choicegroup: (element) ->
$element = $(element) $element = $(element)
...@@ -496,9 +496,9 @@ class @Problem ...@@ -496,9 +496,9 @@ class @Problem
textline: (element) -> textline: (element) ->
$(element).find('input').on 'input', -> $(element).find('input').on 'input', ->
$p = $(element).find('p.status') $p = $(element).find('span.status')
`// Translators: the word unanswered here is about answering a problem the student must solve.` `// Translators: the word unanswered here is about answering a problem the student must solve.`
$p.parent().removeClass("correct incorrect").addClass "unanswered" $p.parent().removeClass("correct incorrect").addClass "unsubmitted"
inputtypeSetupMethods: inputtypeSetupMethods:
......
...@@ -23,6 +23,7 @@ ...@@ -23,6 +23,7 @@
// * +Content - Screenreader Text - Extend // * +Content - Screenreader Text - Extend
// * +Content - Text Wrap - Extend // * +Content - Text Wrap - Extend
// * +Content - Text Truncate - Extend // * +Content - Text Truncate - Extend
// * +Icon - Font-Awesome - Extend
// +Font Sizing - Mixin // +Font Sizing - Mixin
// ==================== // ====================
...@@ -428,3 +429,11 @@ ...@@ -428,3 +429,11 @@
text-overflow: ellipsis; text-overflow: ellipsis;
} }
// * +Icon - Font-Awesome - Extend
// ====================
%use-font-awesome {
display: inline-block;
font-family: FontAwesome;
-webkit-font-smoothing: antialiased;
speak: none;
}
...@@ -66,7 +66,7 @@ class ProblemPage(PageObject): ...@@ -66,7 +66,7 @@ class ProblemPage(PageObject):
""" """
Is there a "correct" status showing? Is there a "correct" status showing?
""" """
return self.q(css="div.problem div.capa_inputtype.textline div.correct p.status").is_present() return self.q(css="div.problem div.capa_inputtype.textline div.correct span.status").is_present()
def click_clarification(self, index=0): def click_clarification(self, index=0):
""" """
......
...@@ -176,11 +176,11 @@ Feature: LMS.Answer problems ...@@ -176,11 +176,11 @@ Feature: LMS.Answer problems
Scenario: I can view and hide the answer if the problem has it: Scenario: I can view and hide the answer if the problem has it:
Given I am viewing a "numerical" that shows the answer "always" Given I am viewing a "numerical" that shows the answer "always"
When I press the button with the label "Show Answer" When I press the button with the label "SHOW ANSWER"
Then the Show/Hide button label is "Hide Answer" Then the Show/Hide button label is "HIDE ANSWER"
And I should see "4.14159" somewhere in the page And I should see "4.14159" somewhere in the page
When I press the button with the label "Hide Answer" When I press the button with the label "HIDE ANSWER"
Then the Show/Hide button label is "Show Answer" Then the Show/Hide button label is "SHOW ANSWER"
And I should not see "4.14159" anywhere on the page And I should not see "4.14159" anywhere on the page
Scenario: I can see my score on a problem when I answer it and after I reset it Scenario: I can see my score on a problem when I answer it and after I reset it
......
...@@ -84,7 +84,7 @@ PROBLEM_DICT = { ...@@ -84,7 +84,7 @@ PROBLEM_DICT = {
'answer': 'correct string'}, 'answer': 'correct string'},
'correct': ['div.correct'], 'correct': ['div.correct'],
'incorrect': ['div.incorrect'], 'incorrect': ['div.incorrect'],
'unanswered': ['div.unanswered']}, 'unanswered': ['div.unanswered', 'div.unsubmitted']},
'numerical': { 'numerical': {
'factory': NumericalResponseXMLFactory(), 'factory': NumericalResponseXMLFactory(),
...@@ -95,7 +95,7 @@ PROBLEM_DICT = { ...@@ -95,7 +95,7 @@ PROBLEM_DICT = {
'math_display': True}, 'math_display': True},
'correct': ['div.correct'], 'correct': ['div.correct'],
'incorrect': ['div.incorrect'], 'incorrect': ['div.incorrect'],
'unanswered': ['div.unanswered']}, 'unanswered': ['div.unanswered', 'div.unsubmitted']},
'formula': { 'formula': {
'factory': FormulaResponseXMLFactory(), 'factory': FormulaResponseXMLFactory(),
...@@ -108,7 +108,7 @@ PROBLEM_DICT = { ...@@ -108,7 +108,7 @@ PROBLEM_DICT = {
'answer': 'x^2+2*x+y'}, 'answer': 'x^2+2*x+y'},
'correct': ['div.correct'], 'correct': ['div.correct'],
'incorrect': ['div.incorrect'], 'incorrect': ['div.incorrect'],
'unanswered': ['div.unanswered']}, 'unanswered': ['div.unanswered', 'div.unsubmitted']},
'script': { 'script': {
'factory': CustomResponseXMLFactory(), 'factory': CustomResponseXMLFactory(),
...@@ -129,7 +129,7 @@ PROBLEM_DICT = { ...@@ -129,7 +129,7 @@ PROBLEM_DICT = {
""")}, """)},
'correct': ['div.correct'], 'correct': ['div.correct'],
'incorrect': ['div.incorrect'], 'incorrect': ['div.incorrect'],
'unanswered': ['div.unanswered']}, 'unanswered': ['div.unanswered', 'div.unsubmitted']},
'code': { 'code': {
'factory': CodeResponseXMLFactory(), 'factory': CodeResponseXMLFactory(),
......
...@@ -28,7 +28,7 @@ h1.top-header { ...@@ -28,7 +28,7 @@ h1.top-header {
.light-button, a.light-button, // only used in askbot as classes .light-button, a.light-button, // only used in askbot as classes
.gray-button { .gray-button {
@include button(simple, #eee); @include button(simple, $gray-l5);
@extend .button-reset; @extend .button-reset;
font-size: em(13); font-size: em(13);
} }
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment