Merge pull request #14421 from edx/christina/wiki
Wiki accessibility fixes.
This commit is contained in:
@@ -21,11 +21,25 @@ class CourseWikiPage(CoursePage):
|
||||
|
||||
def open_editor(self):
|
||||
"""
|
||||
Replace content of a wiki article with new content
|
||||
Display the editor for a wiki article.
|
||||
"""
|
||||
edit_button = self.q(css='.fa-pencil')
|
||||
edit_button.click()
|
||||
|
||||
def show_history(self):
|
||||
"""
|
||||
Show the change history for a wiki article.
|
||||
"""
|
||||
edit_button = self.q(css='.fa-clock-o')
|
||||
edit_button.click()
|
||||
|
||||
def show_children(self):
|
||||
"""
|
||||
Show the children of a wiki article.
|
||||
"""
|
||||
children_link = self.q(css='.see-children>a')
|
||||
children_link.click()
|
||||
|
||||
@property
|
||||
def article_name(self):
|
||||
"""
|
||||
@@ -34,17 +48,15 @@ class CourseWikiPage(CoursePage):
|
||||
return str(self.q(css='.main-article h1').text[0])
|
||||
|
||||
|
||||
class CourseWikiEditPage(CoursePage):
|
||||
"""
|
||||
Editor page
|
||||
"""
|
||||
class CourseWikiSubviewPage(CoursePage): # pylint: disable=abstract-method
|
||||
""" Abstract base page for subviews within the wiki. """
|
||||
|
||||
def __init__(self, browser, course_id, course_info):
|
||||
"""
|
||||
Course ID is currently of the form "edx/999/2013_Spring"
|
||||
but this format could change.
|
||||
"""
|
||||
super(CourseWikiEditPage, self).__init__(browser, course_id)
|
||||
super(CourseWikiSubviewPage, self).__init__(browser, course_id)
|
||||
self.course_id = course_id
|
||||
self.course_info = course_info
|
||||
self.article_name = "{org}.{course_number}.{course_run}".format(
|
||||
@@ -53,6 +65,12 @@ class CourseWikiEditPage(CoursePage):
|
||||
course_run=self.course_info['run']
|
||||
)
|
||||
|
||||
|
||||
class CourseWikiEditPage(CourseWikiSubviewPage):
|
||||
"""
|
||||
Editor page
|
||||
"""
|
||||
|
||||
@property
|
||||
def url_path(self):
|
||||
"""
|
||||
@@ -79,3 +97,41 @@ class CourseWikiEditPage(CoursePage):
|
||||
"""
|
||||
self.q(css='button[name="save"]').click()
|
||||
self.wait_for_element_presence('.alert-success', 'wait for the article to be saved')
|
||||
|
||||
|
||||
class CourseWikiHistoryPage(CourseWikiSubviewPage):
|
||||
"""
|
||||
Course wiki change history page.
|
||||
"""
|
||||
|
||||
def is_browser_on_page(self):
|
||||
"""
|
||||
Return if the browser is on the history page.
|
||||
"""
|
||||
return self.q(css='section.history').present
|
||||
|
||||
@property
|
||||
def url_path(self):
|
||||
"""
|
||||
Construct a URL to the page within the course.
|
||||
"""
|
||||
return "/wiki/" + self.article_name + "/_history"
|
||||
|
||||
|
||||
class CourseWikiChildrenPage(CourseWikiSubviewPage):
|
||||
"""
|
||||
Course wiki "All Children" page.
|
||||
"""
|
||||
|
||||
def is_browser_on_page(self):
|
||||
"""
|
||||
Return if the browser is on the wiki children page (which contains a search widget).
|
||||
"""
|
||||
return self.q(css='.form-search').present
|
||||
|
||||
@property
|
||||
def url_path(self):
|
||||
"""
|
||||
Construct a URL to the page within the course.
|
||||
"""
|
||||
return "/wiki/" + self.article_name + "/_dir"
|
||||
|
||||
@@ -38,7 +38,9 @@ from common.test.acceptance.pages.studio.settings import SettingsPage
|
||||
from common.test.acceptance.pages.lms.login_and_register import CombinedLoginAndRegisterPage, ResetPasswordPage
|
||||
from common.test.acceptance.pages.lms.track_selection import TrackSelectionPage
|
||||
from common.test.acceptance.pages.lms.pay_and_verify import PaymentAndVerificationFlow, FakePaymentPage
|
||||
from common.test.acceptance.pages.lms.course_wiki import CourseWikiPage, CourseWikiEditPage
|
||||
from common.test.acceptance.pages.lms.course_wiki import (
|
||||
CourseWikiPage, CourseWikiEditPage, CourseWikiHistoryPage, CourseWikiChildrenPage
|
||||
)
|
||||
from common.test.acceptance.fixtures.course import CourseFixture, XBlockFixtureDesc, CourseUpdateDesc
|
||||
|
||||
|
||||
@@ -543,7 +545,6 @@ class PayAndVerifyTest(EventsTestMixin, UniqueCourseTest):
|
||||
self.assertEqual(enrollment_mode, 'verified')
|
||||
|
||||
|
||||
@attr(shard=1)
|
||||
class CourseWikiTest(UniqueCourseTest):
|
||||
"""
|
||||
Tests that verify the course wiki.
|
||||
@@ -580,6 +581,14 @@ class CourseWikiTest(UniqueCourseTest):
|
||||
self.course_wiki_page.open_editor()
|
||||
self.course_wiki_edit_page.wait_for_page()
|
||||
|
||||
def _check_for_accessibility_errors(self, page, custom_rules=None):
|
||||
""" Run accessibility check with custom rules, if provided """
|
||||
if custom_rules is not None:
|
||||
page.a11y_audit.config.set_rules(custom_rules)
|
||||
|
||||
page.a11y_audit.check_for_accessibility_errors()
|
||||
|
||||
@attr(shard=1)
|
||||
def test_edit_course_wiki(self):
|
||||
"""
|
||||
Wiki page by default is editable for students.
|
||||
@@ -596,6 +605,47 @@ class CourseWikiTest(UniqueCourseTest):
|
||||
actual_content = unicode(self.course_wiki_page.q(css='.wiki-article p').text[0])
|
||||
self.assertEqual(content, actual_content)
|
||||
|
||||
@attr('a11y')
|
||||
def test_view_a11y(self):
|
||||
"""
|
||||
Verify the basic accessibility of the wiki page as initially displayed.
|
||||
"""
|
||||
self._check_for_accessibility_errors(self.course_wiki_page)
|
||||
|
||||
@attr('a11y')
|
||||
def test_edit_a11y(self):
|
||||
"""
|
||||
Verify the basic accessibility of edit wiki page.
|
||||
"""
|
||||
self._open_editor()
|
||||
self._check_for_accessibility_errors(self.course_wiki_edit_page)
|
||||
|
||||
@attr('a11y')
|
||||
def test_changes_a11y(self):
|
||||
"""
|
||||
Verify the basic accessibility of changes wiki page.
|
||||
"""
|
||||
self.course_wiki_page.show_history()
|
||||
history_page = CourseWikiHistoryPage(self.browser, self.course_id, self.course_info)
|
||||
history_page.wait_for_page()
|
||||
self._check_for_accessibility_errors(history_page)
|
||||
|
||||
@attr('a11y')
|
||||
def test_children_a11y(self):
|
||||
"""
|
||||
Verify the basic accessibility of changes wiki page.
|
||||
"""
|
||||
self.course_wiki_page.show_children()
|
||||
children_page = CourseWikiChildrenPage(self.browser, self.course_id, self.course_info)
|
||||
children_page.wait_for_page()
|
||||
custom_rules = {
|
||||
'ignore': [
|
||||
'label', # TNL-6440
|
||||
'data-table' # TNL-6439
|
||||
]
|
||||
}
|
||||
self._check_for_accessibility_errors(children_page, custom_rules)
|
||||
|
||||
|
||||
@attr(shard=1)
|
||||
class HighLevelTabTest(UniqueCourseTest):
|
||||
|
||||
@@ -10,6 +10,12 @@ $(document).ready(function() {
|
||||
// Store the inital contents so we can compare for unsaved changes
|
||||
var initial_contents = editor.getValue();
|
||||
|
||||
// The Wiki associates a label with the text area that has ID "id_content". However, when we swap in
|
||||
// CodeMirror, that text area is hidden. We need to associate the label with visible CodeMirror text area
|
||||
// (and there is JS code in the wiki that depends on "id_content" interact with the content, so we have
|
||||
// to leave that alone).
|
||||
editor.getInputField().setAttribute('id', 'id_codemirror_content');
|
||||
$(".control-label[for='id_content']")[0].setAttribute('for', 'id_codemirror_content');
|
||||
window.onbeforeunload = function askConfirm() { // Warn the user before they navigate away
|
||||
if (editor.getValue() != initial_contents) {
|
||||
return 'You have made changes to the article that have not been saved yet.';
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
section.wiki {
|
||||
padding-top: 25px;
|
||||
.wiki-wrapper section.wiki {
|
||||
|
||||
.pull-left {
|
||||
float: left;
|
||||
@@ -9,20 +8,17 @@ section.wiki {
|
||||
float: right;
|
||||
}
|
||||
|
||||
.wiki-wrapper {
|
||||
@include clearfix();
|
||||
@include clearfix();
|
||||
|
||||
> header {
|
||||
height: 33px;
|
||||
padding: 24px 0 26px;
|
||||
border-bottom: 1px solid $gray-l3;
|
||||
border-radius: 3px 3px 0 0;
|
||||
background-color: $sidebar-color;
|
||||
}
|
||||
.breadcrumbs-header {
|
||||
height: 33px;
|
||||
padding: 24px 0 26px;
|
||||
border-bottom: 1px solid $gray-l3;
|
||||
border-radius: 3px 3px 0 0;
|
||||
background-color: $sidebar-color;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*-----------------
|
||||
|
||||
Breadcrumbs
|
||||
@@ -126,10 +122,6 @@ section.wiki {
|
||||
|
||||
-----------------*/
|
||||
|
||||
.article-wrapper {
|
||||
|
||||
}
|
||||
|
||||
h1 {
|
||||
font-weight: bold;
|
||||
letter-spacing: 0;
|
||||
@@ -238,7 +230,7 @@ section.wiki {
|
||||
|
||||
.label {
|
||||
font-size: 0.7em;
|
||||
color: #aaa;
|
||||
color: $uxpl-gray-base;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
@@ -261,8 +253,8 @@ section.wiki {
|
||||
line-height: 25px;
|
||||
|
||||
&:hover, &:focus {
|
||||
background-color: $gray-l6;
|
||||
text-decoration: none;
|
||||
color: $uxpl-blue-hover-active;
|
||||
font-weight: bold;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -276,9 +268,11 @@ section.wiki {
|
||||
li {
|
||||
&.active {
|
||||
a {
|
||||
color: $link-color;
|
||||
color: $uxpl-blue-hover-active;
|
||||
font-weight: bold;
|
||||
background-color: $gray-l4;
|
||||
background-color: $light-gray1;
|
||||
border-color: $uxpl-blue-hover-active;
|
||||
border-left: 4px solid
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -301,8 +295,8 @@ section.wiki {
|
||||
}
|
||||
|
||||
&:hover, &:focus {
|
||||
background-color: $gray-l6;
|
||||
text-decoration: none;
|
||||
color: $uxpl-blue-hover-active;
|
||||
font-weight: bold;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -322,7 +316,7 @@ section.wiki {
|
||||
font-weight: bold;
|
||||
font-style: normal;
|
||||
text-transform: uppercase;
|
||||
color: #aaa;
|
||||
color: $uxpl-gray-base;
|
||||
}
|
||||
|
||||
input {
|
||||
@@ -380,7 +374,7 @@ section.wiki {
|
||||
left: 7px;
|
||||
font-family: $sans-serif;
|
||||
font-size: 0.75em;
|
||||
color: #aaa;
|
||||
color: $uxpl-gray-base;
|
||||
pointer-events: none;
|
||||
}
|
||||
}
|
||||
@@ -422,7 +416,7 @@ section.wiki {
|
||||
}
|
||||
|
||||
&.btn-primary {
|
||||
@include button;
|
||||
@include button(simple, $btn-default-color);
|
||||
font-size: 0.8em;
|
||||
}
|
||||
|
||||
@@ -606,7 +600,7 @@ section.wiki {
|
||||
|
||||
.accordion small {
|
||||
font-size: 0.8em;
|
||||
color: #aaa;
|
||||
color: $uxpl-gray-base;
|
||||
}
|
||||
|
||||
.accordion-toggle div {
|
||||
@@ -747,7 +741,7 @@ section.wiki {
|
||||
|
||||
+ p {
|
||||
font-size: 0.9em;
|
||||
color: #aaa;
|
||||
color: $uxpl-gray-base;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -757,7 +751,7 @@ section.wiki {
|
||||
font-size: .9em;
|
||||
|
||||
a {
|
||||
color: #aaa;
|
||||
color: $uxpl-gray-base;
|
||||
|
||||
&:hover, &:focus {
|
||||
color: #777;
|
||||
@@ -831,7 +825,7 @@ section.wiki {
|
||||
float: right;
|
||||
font-size: 0.6em;
|
||||
line-height: 20px;
|
||||
color: #aaa;
|
||||
color: $uxpl-gray-base;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -56,29 +56,31 @@
|
||||
{% endwith %}
|
||||
{% endif %}
|
||||
|
||||
<main id="main" aria-label="Content" tabindex="-1">
|
||||
<section class="container wiki {{ selected_tab }}" id="wiki-content">
|
||||
<div class="wiki-wrapper">
|
||||
{% block wiki_body %}
|
||||
<div class="container">
|
||||
<div class="wiki-wrapper">
|
||||
<main id="main" aria-label="Content" tabindex="-1">
|
||||
<section class="wiki {{ selected_tab }}" id="wiki-content">
|
||||
{% block wiki_body %}
|
||||
|
||||
{% block wiki_breadcrumbs %}{% endblock %}
|
||||
{% block wiki_breadcrumbs %}{% endblock %}
|
||||
|
||||
{% if messages %}
|
||||
{% for message in messages %}
|
||||
<div class="alert alert-{{ message.tags }}">
|
||||
<a class="close" data-dismiss="alert" href="#">×</a>
|
||||
{{ message }}
|
||||
</div>
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
{% if messages %}
|
||||
{% for message in messages %}
|
||||
<div class="alert alert-{{ message.tags }}">
|
||||
<a class="close" data-dismiss="alert" href="#">×</a>
|
||||
{{ message }}
|
||||
</div>
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
|
||||
{% block wiki_contents %}{% endblock %}
|
||||
{% block wiki_contents %}{% endblock %}
|
||||
|
||||
{% endblock %}
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
||||
</section>
|
||||
</main>
|
||||
</section>
|
||||
</main>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{% endblock %}
|
||||
{% endwith %}
|
||||
|
||||
@@ -43,8 +43,8 @@
|
||||
</a>
|
||||
|
||||
</div>
|
||||
<section id="previewModal" class="modal" aria-hidden="true">
|
||||
<div class="inner-wrapper" role="dialog" aria-labelledby="preview-title">
|
||||
<div id="previewModal" class="modal" aria-hidden="true">
|
||||
<div class="inner-wrapper" role="dialog" aria-labelledby="preview-title" aria-modal=true>
|
||||
<button class="close-modal"><span class="icon fa fa-remove" aria-hidden="true"></span> <span class="sr">{% trans 'Close' %}</span></button>
|
||||
|
||||
<header>
|
||||
@@ -52,21 +52,23 @@
|
||||
<hr/>
|
||||
</header>
|
||||
|
||||
<div class="modal-body">
|
||||
<iframe name="previewWindow" frameborder="0"></iframe>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="submit" name="save" value="1" class="btn btn-large btn-primary" onclick="this.form.target=''; this.form.action='{% url 'wiki:edit' path=urlpath.path article_id=article.id %}'">
|
||||
<span class="icon fa fa-check-square-o" aria-hidden="true"></span>
|
||||
{% trans "Save changes" %}
|
||||
</button>
|
||||
|
||||
<a id="previewModalBackToEditor" href="#" class="btn btn-large">
|
||||
<span class="icon fa fa-circle-arrow-left" aria-hidden="true"></span>
|
||||
{% trans "Back to editor" %}
|
||||
</a>
|
||||
<div class="modal-body">
|
||||
<iframe name="previewWindow" frameborder="0"></iframe>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="submit" name="save" value="1" class="btn btn-large btn-primary" onclick="this.form.target=''; this.form.action='{% url 'wiki:edit' path=urlpath.path article_id=article.id %}'">
|
||||
<span class="icon fa fa-check-square-o" aria-hidden="true"></span>
|
||||
{% trans "Save changes" %}
|
||||
</button>
|
||||
|
||||
<a id="previewModalBackToEditor" href="#" class="btn btn-large">
|
||||
<span class="icon fa fa-circle-arrow-left" aria-hidden="true"></span>
|
||||
{% trans "Back to editor" %}
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
{% include "wiki/includes/cheatsheet.html" %}
|
||||
</form>
|
||||
|
||||
|
||||
@@ -199,8 +199,8 @@
|
||||
|
||||
</div>
|
||||
<input type="hidden" name="r" value="" />
|
||||
<section id="previewRevisionModal" class="modal" aria-hidden="true">
|
||||
<div class="inner-wrapper" role="dialog" aria-labelledby="preview-title">
|
||||
<div id="previewRevisionModal" class="modal" aria-hidden="true">
|
||||
<div class="inner-wrapper" role="dialog" aria-labelledby="preview-title" aria-modal=true>
|
||||
<button class="close-modal"><span class="icon fa fa-remove" aria-hidden="true"></span> <span class="sr">{% trans 'Close' %}</span></button>
|
||||
|
||||
<header>
|
||||
@@ -228,10 +228,10 @@
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
|
||||
<section id="mergeModal" class="modal" aria-hidden="true">
|
||||
<div class="inner-wrapper" role="dialog" aria-labelledby="merge-title">
|
||||
<div id="mergeModal" class="modal" aria-hidden="true">
|
||||
<div class="inner-wrapper" role="dialog" aria-labelledby="merge-title" aria-modal=true>
|
||||
<button class="close-modal"><span class="icon fa fa-remove" aria-hidden="true"></span> <span class="sr">{% trans 'Close' %}</span></button>
|
||||
|
||||
<header>
|
||||
|
||||
@@ -6,7 +6,8 @@
|
||||
%>
|
||||
|
||||
%if urlpath is not Undefined and urlpath:
|
||||
<header>
|
||||
<header class="breadcrumbs-header">
|
||||
<h2 class="sr">${_("Course Wiki")}</h2>
|
||||
<ul class="breadcrumb pull-left" class="">
|
||||
<%
|
||||
# The create button links to the highest ancestor we have edit priveleges to
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{% load i18n %}
|
||||
<section id="cheatsheetModal" class="modal" aria-hidden="true">
|
||||
<div class="inner-wrapper" role="dialog" aria-labelledby="cheatsheet-title">
|
||||
<div id="cheatsheetModal" class="modal" aria-hidden="true">
|
||||
<div class="inner-wrapper" role="dialog" aria-labelledby="cheatsheet-title" aria-modal=true>
|
||||
<button class="close-modal"><span class="icon fa fa-remove" aria-hidden="true"></span> <span class="sr">{% trans 'Close' %}</span></button>
|
||||
|
||||
<header>
|
||||
@@ -60,4 +60,4 @@ http://wikipedia.org
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
|
||||
@@ -9,34 +9,3 @@
|
||||
});
|
||||
</script>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<div>
|
||||
{% for plugin in sidebar %}
|
||||
|
||||
<div class="accordion" id="accordion_{{ plugin.slug }}">
|
||||
|
||||
<div class="accordion-group">
|
||||
|
||||
<div class="accordion-heading">
|
||||
<a class="accordion-toggle" href="#collapse_{{ plugin.slug }}" data-toggle="collapse">
|
||||
<h2>{{ plugin.sidebar.headline }} <span class="{{ plugin.sidebar.icon_class }}"></span></h2>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div id="collapse_{{ plugin.slug }}" class="accordion-body collapse{% if form_images.errors %} in{% endif %}">
|
||||
<div class="accordion-inner form-vertical">
|
||||
{% if plugin.sidebar.template %}
|
||||
{% with form_images as form and plugin as plugin %}
|
||||
{% include plugin.sidebar.template %}
|
||||
{% endwith %}
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
"underscore.string": "~3.3.4"
|
||||
},
|
||||
"devDependencies": {
|
||||
"edx-custom-a11y-rules": "0.1.2",
|
||||
"edx-custom-a11y-rules": "0.1.3",
|
||||
"eslint": "^2.13.1",
|
||||
"eslint-config-edx": "^1.2.1",
|
||||
"jasmine-core": "^2.4.1",
|
||||
|
||||
Reference in New Issue
Block a user