Commit fec2fb4e by Christina Roberts Committed by GitHub

Merge pull request #14421 from edx/christina/wiki

Wiki accessibility fixes.
parents a46aec9b 95672ec2
......@@ -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.';
......
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 %}
{% block wiki_breadcrumbs %}{% endblock %}
{% if messages %}
{% for message in messages %}
<div class="alert alert-{{ message.tags }}">
<a class="close" data-dismiss="alert" href="#">&times;</a>
{{ message }}
</div>
{% endfor %}
{% endif %}
{% block wiki_contents %}{% endblock %}
{% endblock %}
</div>
</section>
</main>
<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 %}
{% if messages %}
{% for message in messages %}
<div class="alert alert-{{ message.tags }}">
<a class="close" data-dismiss="alert" href="#">&times;</a>
{{ message }}
</div>
{% endfor %}
{% endif %}
{% block wiki_contents %}{% endblock %}
{% endblock %}
</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
......
{% 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",
......
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