Commit 8eefa571 authored by webchick's avatar webchick

Issue #849926 by jessebeach, Jacine, mgifford, casey, Mirabuck: Fixed links...

Issue #849926 by jessebeach, Jacine, mgifford, casey, Mirabuck: Fixed links wrapped in .contextual-links-wrapper divs are not accessible at all via keyboard alone also problems with screen readers.
parent 5c90f1b8
/**
* @file
* RTL base styles for the Contextual module.
*/
.contextual .trigger {
text-align: left;
}
......@@ -4,35 +4,36 @@
* Generic base styles for contextual module.
*/
/**
* Contextual links behavior.
*/
.contextual,
.contextual .contextual-links,
.contextual .trigger {
.contextual-region {
position: relative;
}
.touch .contextual .trigger {
display: block;
}
.contextual .contextual-links {
display: none;
}
.touch .contextual,
.touch .contextual .trigger,
.no-touch .contextual-region:hover .contextual,
.no-touch .contextual-region:hover .contextual-links-trigger-active,
.contextual-active .contextual-links {
.contextual-links-active .contextual-links {
display: block;
}
/**
* Contextual links structure.
* The .element-focusable class extends the .element-invisible class to allow
* the element to be focusable when navigated to via the keyboard.
*
* Add support for hover.
*/
.contextual-region {
position: relative;
.touch .contextual-region .element-invisible.element-focusable,
.contextual-region:hover .element-invisible.element-focusable {
clip: auto;
overflow: visible;
height: auto;
}
.contextual {
position: absolute;
z-index: 999;
}
.contextual .trigger {
overflow: hidden;
position: relative;
text-align: right; /* LTR */
z-index: 1;
/* Override the position for contextual links. */
.contextual-region .element-invisible.element-focusable:active,
.contextual-region .element-invisible.element-focusable:focus,
.contextual-region:hover .element-invisible.element-focusable,
.contextual-region-active .element-invisible.element-focusable,
.touch .contextual-region .element-invisible.element-focusable {
position: relative !important;
}
......@@ -3,55 +3,159 @@
* Attaches behaviors for the Contextual module.
*/
(function ($) {
(function ($, Drupal) {
"use strict";
Drupal.contextualLinks = Drupal.contextualLinks || {};
/**
* Attaches outline behavior for regions associated with contextual links.
*/
Drupal.behaviors.contextualLinks = {
Drupal.behaviors.contextual = {
attach: function (context) {
$(context).find('div.contextual').once('contextual-links', function () {
var $wrapper = $(this);
var $region = $wrapper.closest('.contextual-region');
var $links = $wrapper.find('ul');
var $trigger = $('<a class="trigger" href="#" />').text(Drupal.t('Configure')).click(
function (e) {
e.preventDefault();
e.stopPropagation();
$links.stop(true, true).slideToggle(100);
$wrapper.toggleClass('contextual-active');
}
);
// Attach hover behavior to trigger and ul.contextual-links, for non touch devices only.
if(!Modernizr.touch) {
$trigger.add($links).hover(
function () { $region.addClass('contextual-region-active'); },
function () { $region.removeClass('contextual-region-active'); }
);
}
// Hide the contextual links when user clicks a link or rolls out of the .contextual-region.
$region.bind('mouseleave click', Drupal.contextualLinks.mouseleave);
$region.hover(
function() { $trigger.addClass('contextual-links-trigger-active'); },
function() { $trigger.removeClass('contextual-links-trigger-active'); }
);
// Prepend the trigger.
$wrapper.prepend($trigger);
$('ul.contextual-links', context).once('contextual', function () {
var $this = $(this);
$this.data('drupal-contextual', new Drupal.contextual($this, $this.closest('.contextual-region')));
});
}
};
/**
* Contextual links object.
*/
Drupal.contextual = function($links, $region) {
this.$links = $links;
this.$region = $region;
this.init();
};
/**
* Initiates a contextual links object.
*/
Drupal.contextual.prototype.init = function() {
// Wrap the links to provide positioning and behavior attachment context.
this.$wrapper = $(Drupal.theme.contextualWrapper())
.insertBefore(this.$links)
.append(this.$links);
// Mark the links as hidden. Use aria-role form so that the number of items
// in the list is spoken.
this.$links
.attr({
'hidden': 'hidden',
'role': 'form'
});
// Create and append the contextual links trigger.
var action = Drupal.t('Open');
this.$trigger = $(Drupal.theme.contextualTrigger())
.text(Drupal.t('@action configuration options', {'@action': action}))
// Set the aria-pressed state.
.attr('aria-pressed', false)
.prependTo(this.$wrapper);
// Bind behaviors through delegation.
var highlightRegion = $.proxy(this.highlightRegion, this);
this.$region
.on('click.contextual', '.contextual .trigger', $.proxy(this.triggerClickHandler, this))
.on('mouseenter.contextual', {highlight: true}, highlightRegion)
.on('mouseleave.contextual', {highlight: false}, highlightRegion)
.on('mouseleave.contextual', '.contextual', {show: false}, $.proxy(this.triggerLeaveHandler, this))
.on('focus.contextual', '.contextual-links a, .contextual .trigger', {highlight: true}, highlightRegion)
.on('blur.contextual', '.contextual-links a, .contextual .trigger', {highlight: false}, highlightRegion);
};
/**
* Toggles the highlighting of a contextual region.
*
* @param {Object} event
* jQuery Event object.
*/
Drupal.contextual.prototype.highlightRegion = function(event) {
// Set up a timeout to delay the dismissal of the region highlight state.
if (!event.data.highlight && this.timer === undefined) {
return this.timer = window.setTimeout($.proxy($.fn.trigger, $(event.target), 'mouseleave.contextual'), 100);
}
// Clear the timeout to prevent an infinite loop of mouseleave being
// triggered.
if (this.timer) {
window.clearTimeout(this.timer);
delete this.timer;
}
// Toggle active state of the contextual region based on the highlight value.
this.$region.toggleClass('contextual-region-active', event.data.highlight);
// Hide the links if the contextual region is inactive.
var state = this.$region.hasClass('contextual-region-active');
if (!state) {
this.showLinks(state);
}
};
/**
* Handles click on the contextual links trigger.
*
* @param {Object} event
* jQuery Event object.
*/
Drupal.contextual.prototype.triggerClickHandler = function (event) {
event.preventDefault();
this.showLinks();
};
/**
* Handles mouseleave on the contextual links trigger.
*
* @param {Object} event
* jQuery Event object.
*/
Drupal.contextual.prototype.triggerLeaveHandler = function (event) {
var show = event && event.data && event.data.show;
this.showLinks(show);
};
/**
* Toggles the active state of the contextual links.
*
* @param {Boolean} show
* (optional) True if the links should be shown. False is the links should be
* hidden.
*/
Drupal.contextual.prototype.showLinks = function(show) {
this.$wrapper.toggleClass('contextual-links-active', show);
var isOpen = this.$wrapper.hasClass('contextual-links-active');
var action = (isOpen) ? Drupal.t('Close') : Drupal.t('Open');
this.$trigger
.text(Drupal.t('@action configuration options', {'@action': action}))
// Set the aria-pressed state.
.attr('aria-pressed', isOpen);
// Mark the links as hidden if they are.
if (isOpen) {
this.$links.removeAttr('hidden');
}
else {
this.$links.attr('hidden', 'hidden');
}
};
/**
* Wraps contextual links.
*
* @return {String}
* A string representing a DOM fragment.
*/
Drupal.theme.contextualWrapper = function () {
return '<div class="contextual" />';
};
/**
* Disables outline for the region contextual links are associated with.
* A trigger is an interactive element often bound to a click handler.
*
* @return {String}
* A string representing a DOM fragment.
*/
Drupal.contextualLinks.mouseleave = function () {
$(this)
.find('.contextual-active').removeClass('contextual-active')
.find('.contextual-links').hide();
Drupal.theme.contextualTrigger = function () {
return '<button class="trigger element-invisible element-focusable" type="button"></button>';
};
})(jQuery);
})(jQuery, Drupal);
......@@ -69,8 +69,6 @@ function contextual_element_info() {
'#pre_render' => array('contextual_pre_render_links'),
'#theme' => 'links__contextual',
'#links' => array(),
'#prefix' => '<div class="contextual">',
'#suffix' => '</div>',
'#attributes' => array('class' => array('contextual-links')),
'#attached' => array(
'library' => array(
......
......@@ -3,17 +3,26 @@
* RTL styling for contextual module.
*/
/**
* Contextual links wrappers.
*/
.contextual {
left: 5px;
right: auto;
}
.contextual .contextual-links {
border-radius: 0 4px 4px 4px;
left: 0;
right: auto;
}
.contextual-region .contextual .contextual-links a {
text-align: right;
padding: 0.4em 0.6em 0.4em 0.8em;
/**
* Contextual trigger.
*/
.contextual .trigger {
float: left;
}
/**
* Contextual links.
*/
.contextual .contextual-links {
border-radius: 0 4px 4px 4px;
float: left;
text-align: right;
}
......@@ -6,58 +6,64 @@
/**
* Contextual links wrappers.
*/
.contextual {
position: absolute;
right: 0; /* LTR */
top: 2px;
z-index: 999;
}
.contextual-region-active {
outline: 1px dashed #d6d6d6;
outline-offset: 1px;
}
.contextual {
right: 2px; /* LTR */
top: 2px;
}
/**
* Contextual trigger.
*/
.contextual .trigger {
background: transparent url(images/gear-select.png) no-repeat 2px 0;
background: transparent url("images/gear-select.png") no-repeat 2px 0;
border: 1px solid transparent;
height: 18px;
border-radius: 4px 4px 0 0;
/* Override the .element-focusable height: auto */
height: 18px !important;
float: right; /* LTR */
margin: 0;
outline: none;
overflow: hidden;
padding: 0 2px;
text-indent: 34px;
width: 28px;
position: relative;
width: 34px;
text-indent: -9999px;
z-index: 2;
}
.no-touch .contextual .trigger:hover,
.contextual-active .trigger {
.contextual-links-active .trigger {
background-position: 2px -18px;
}
.contextual-active .trigger {
background-color: #ffffff;
.contextual-links-active .trigger {
background-color: #fff;
border-bottom: none;
border-color: #d6d6d6;
border-radius: 4px 4px 0 0;
position: relative;
z-index: 1;
}
/**
* Contextual links.
*
* The following selectors are heavy to discourage theme overriding.
*/
.contextual .contextual-links {
.contextual-region .contextual .contextual-links {
background-color: #fff;
border: 1px solid #d6d6d6;
border-radius: 4px 0 4px 4px; /* LTR */
clear: both;
float: right; /* LTR */
margin: 0;
padding: 0.25em 0;
position: absolute;
right: 0; /* LTR */
text-align: left;
top: 18px;
position: relative;
text-align: left; /* LTR */
top: -1px;
white-space: nowrap;
z-index: 1;
}
/* Reset the li to prevent accidential overrides by a theme. */
.contextual-region .contextual .contextual-links li {
background-color: #fff;
border: none;
......@@ -65,21 +71,22 @@
list-style-image: none;
margin: 0;
padding: 0;
line-height: 100%;
}
.contextual-region .contextual .contextual-links a {
background-color: #fff;
/* This is an unforetunately necessary use of !important to prevent white
* links on a white background or some similar illegible combination. */
color: #333 !important;
display: block;
font-family: sans-serif;
font-size: small;
line-height: 0.8em;
margin: 0.25em 0;
padding: 0.4em 0.8em 0.4em 0.6em; /* LTR */
padding: 0.4em 0.6em;
}
.contextual-region .contextual .contextual-links a,
.no-touch .contextual-region .contextual .contextual-links a:hover,
.contextual-region .contextual .contextual-links a:active,
.contextual-region .contextual .contextual-links a:focus {
background-color: #fff;
color: #333;
.contextual-region .contextual .contextual-links a:hover {
text-decoration: none;
}
.no-touch .contextual-region .contextual .contextual-links li a:hover {
......
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