Skip to content

Commit

Permalink
improve accessibility
Browse files Browse the repository at this point in the history
  • Loading branch information
LukaszWatroba committed Feb 12, 2015
1 parent 0035f54 commit 49b8d5b
Show file tree
Hide file tree
Showing 16 changed files with 181 additions and 85 deletions.
26 changes: 23 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@

- Allows for a nested structure
- Works with (or without) `ng-repeat`
- Allows multiple sections open at once
- Allows multiple sections to be open at once
- Optimized for mobile devices


Expand Down Expand Up @@ -34,10 +34,10 @@

- Put the following markup in your template:
```html
<!-- add `multiple` attribute to allow multiple sections open at once -->
<!-- add `multiple` attribute to allow multiple sections to open at once -->
<v-accordion class="vAccordion--dafault" multiple>

<!-- add expanded attribute to open first section -->
<!-- add expanded attribute to open the section -->
<v-pane expanded>
<v-pane-header>
Pane header #1
Expand Down Expand Up @@ -156,3 +156,23 @@ $scope.collapseCallback = function (index) {
};
```

## Accessibility
vAccordion manages keyboard focus and adds some common aria-* attributes. BUT you should additionally place the `aria-controls` and `aria-labelledby` as follows:

```html
<v-accordion>

<v-pane ng-repeat="pane in panes">
<v-pane-header id="pane{{$index}}-header" aria-controls="pane{{$index}}-content">
{{ pane.header }}
</v-pane-header>

<v-pane-content id="pane{{$index}}-content" aria-labelledby="pane{{$index}}-header">
{{ pane.content }}
</v-pane-content>
</v-pane>

</v-accordion>
```


23 changes: 14 additions & 9 deletions dist/v-accordion.css
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
/**
* vAccordion - AngularJS multi-level accordion component
* @version v1.0.0
* @version v1.1.0
* @link http://lukaszwatroba.github.io/v-accordion
* @author Łukasz Wątroba <l@lukaszwatroba.com>
* @license MIT License, http://www.opensource.org/licenses/MIT
Expand All @@ -26,13 +26,20 @@ v-accordion {

v-pane {
display: block; }
v-pane.is-expanded > v-pane-content > v-pane-content-inner {
v-pane.is-expanded > v-pane-content > div {
visibility: visible; }

v-pane-header {
display: block;
position: relative;
cursor: pointer; }
cursor: pointer;
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
outline: none; }
v-pane-header:focus {
outline: none; }

v-pane-header-inner {
display: block; }
Expand All @@ -42,10 +49,8 @@ v-pane-content {
position: relative;
overflow: hidden;
max-height: 0px; }

v-pane-content-inner {
display: block;
visibility: none; }
v-pane-content > div {
visibility: none; }

/* Theme: default
***************************************/
Expand All @@ -66,7 +71,7 @@ v-pane-content-inner {
-webkit-transform: rotate(0deg);
-ms-transform: rotate(0deg);
transform: rotate(0deg); }
.vAccordion--default v-pane.is-expanded > v-pane-content > v-pane-content-inner {
.vAccordion--default v-pane.is-expanded > v-pane-content > div {
-webkit-transform: translate3d(0, 0, 0);
transform: translate3d(0, 0, 0); }
.vAccordion--default v-pane.is-expanded-add > v-pane-content, .vAccordion--default v-pane.is-expanded-remove > v-pane-content {
Expand Down Expand Up @@ -98,7 +103,7 @@ v-pane-content-inner {
transform: rotate(-90deg); }
.vAccordion--default v-pane-header:hover {
color: #2196F3; }
.vAccordion--default v-pane-content-inner {
.vAccordion--default v-pane-content > div {
padding-bottom: 20px;
will-change: transform, opacity;
-webkit-transform: translate3d(0, 30px, 0);
Expand Down
81 changes: 57 additions & 24 deletions dist/v-accordion.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
/**
* vAccordion - AngularJS multi-level accordion component
* @version v1.0.0
* @version v1.1.0
* @link http://lukaszwatroba.github.io/v-accordion
* @author Łukasz Wątroba <l@lukaszwatroba.com>
* @license MIT License, http://www.opensource.org/licenses/MIT
Expand Down Expand Up @@ -50,7 +50,7 @@ function vAccordionDirective () {
transclude(scope.$parent, function(clone) {
iElement.append(clone);
});

var protectedApiMethods = ['toggle', 'expand', 'collapse', 'expandAll', 'collapseAll'];

function checkCustomControlAPIMethods () {
Expand All @@ -65,6 +65,12 @@ function vAccordionDirective () {
scope.allowMultiple = angular.isDefined(iAttrs.multiple);
}

iAttrs.$set('role', 'tablist');

if (scope.allowMultiple) {
iAttrs.$set('aria-multiselectable', 'true');
}

if (angular.isDefined(scope.control)) {
checkCustomControlAPIMethods();

Expand All @@ -86,7 +92,8 @@ function AccordionDirectiveController ($scope) {

$scope.panes = [];

function hasExpandedPane () {

ctrl.hasExpandedPane = function () {
var bool = false;

for (var i = 0, length = $scope.panes.length; i < length; i++) {
Expand All @@ -99,15 +106,16 @@ function AccordionDirectiveController ($scope) {
}

return bool;
}

function getPaneByIndex (index) {
};
ctrl.getPaneByIndex = function (index) {
return $scope.panes[index];
}
};

function getPaneIndex (pane) {
ctrl.getPaneIndex = function (pane) {
return $scope.panes.indexOf(pane);
}
};


ctrl.disable = function () {
isDisabled = true;
Expand All @@ -119,15 +127,15 @@ function AccordionDirectiveController ($scope) {

ctrl.addPane = function (paneToAdd) {
if (!$scope.allowMultiple) {
if (hasExpandedPane() && paneToAdd.isExpanded) {
if (ctrl.hasExpandedPane() && paneToAdd.isExpanded) {
throw new Error('The `multiple` attribute can\'t be found');
}
}

$scope.panes.push(paneToAdd);

if (paneToAdd.isExpanded) {
$scope.expandCb({ index: getPaneIndex(paneToAdd) });
$scope.expandCb({ index: ctrl.getPaneIndex(paneToAdd) });
}
};

Expand All @@ -141,9 +149,9 @@ function AccordionDirectiveController ($scope) {
paneToToggle.isExpanded = !paneToToggle.isExpanded;

if (paneToToggle.isExpanded) {
$scope.expandCb({ index: getPaneIndex(paneToToggle) });
$scope.expandCb({ index: ctrl.getPaneIndex(paneToToggle) });
} else {
$scope.collapseCb({ index: getPaneIndex(paneToToggle) });
$scope.collapseCb({ index: ctrl.getPaneIndex(paneToToggle) });
}
};

Expand All @@ -156,7 +164,7 @@ function AccordionDirectiveController ($scope) {

if (!paneToExpand.isExpanded) {
paneToExpand.isExpanded = true;
$scope.expandCb({ index: getPaneIndex(paneToExpand) });
$scope.expandCb({ index: ctrl.getPaneIndex(paneToExpand) });
}
};

Expand All @@ -165,7 +173,7 @@ function AccordionDirectiveController ($scope) {

if (paneToCollapse.isExpanded) {
paneToCollapse.isExpanded = false;
$scope.collapseCb({ index: getPaneIndex(paneToCollapse) });
$scope.collapseCb({ index: ctrl.getPaneIndex(paneToCollapse) });
}
};

Expand Down Expand Up @@ -194,13 +202,13 @@ function AccordionDirectiveController ($scope) {
// API
$scope.internalControl = {
toggle: function (index) {
ctrl.toggle( getPaneByIndex(index) );
ctrl.toggle( ctrl.getPaneByIndex(index) );
},
expand: function (index) {
ctrl.expand( getPaneByIndex(index) );
ctrl.expand( ctrl.getPaneByIndex(index) );
},
collapse: function (index) {
ctrl.collapse( getPaneByIndex(index) );
ctrl.collapse( ctrl.getPaneByIndex(index) );
},
expandAll: ctrl.expandAll,
collapseAll: ctrl.collapseAll
Expand All @@ -221,9 +229,11 @@ function vPaneContentDirective () {
restrict: 'E',
require: '^vPane',
transclude: true,
template: '<v-pane-content-inner ng-transclude></v-pane-content-inner>',
template: '<div ng-transclude></div>',
scope: {},
link: function () {}
link: function (scope, iElement, iAttrs) {
iAttrs.$set('role', 'tabpanel');
}
};
}

Expand All @@ -240,9 +250,11 @@ function vPaneHeaderDirective () {
restrict: 'E',
require: '^vPane',
transclude: true,
template: '<v-pane-header-inner ng-transclude></v-pane-header-inner>',
template: '<div ng-transclude></div>',
scope: {},
link: function (scope, iElement, iAttrs, paneCtrl) {
iAttrs.$set('role', 'tab');

iElement.on('click', function () {
scope.$apply(function () {
paneCtrl.toggle();
Expand Down Expand Up @@ -282,7 +294,7 @@ function vPaneDirective ($timeout, $animate, accordionConfig) {

var paneHeader = iElement.find('v-pane-header'),
paneContent = iElement.find('v-pane-content'),
paneInner = iElement.find('v-pane-content-inner');
paneInner = paneContent.find('div');

if (!paneHeader[0]) {
throw new Error('The `v-pane-header` directive can\'t be found');
Expand All @@ -295,11 +307,14 @@ function vPaneDirective ($timeout, $animate, accordionConfig) {
accordionCtrl.addPane(scope);
scope.accordionCtrl = accordionCtrl;

paneContent[0].style.maxHeight = '0px';

function expand () {
accordionCtrl.disable();

paneContent[0].style.maxHeight = '0px';
paneHeader.attr({
'aria-selected': 'true',
'tabindex': 0
});

$timeout(function () {
$animate.addClass(iElement, states.expanded)
Expand All @@ -316,7 +331,12 @@ function vPaneDirective ($timeout, $animate, accordionConfig) {

function collapse () {
accordionCtrl.disable();

paneContent[0].style.maxHeight = paneInner[0].offsetHeight + 'px';
paneHeader.attr({
'aria-selected': 'false',
'tabindex': -1
});

$timeout(function () {
$animate.removeClass(iElement, states.expanded)
Expand All @@ -333,6 +353,19 @@ function vPaneDirective ($timeout, $animate, accordionConfig) {
if (scope.isExpanded) {
iElement.addClass(states.expanded);
paneContent[0].style.maxHeight = 'none';

paneHeader.attr({
'aria-selected': 'true',
'tabindex': 0
});
} else {
paneHeader.attr('aria-selected', 'false');
paneContent[0].style.maxHeight = '0px';

paneHeader.attr({
'aria-selected': 'false',
'tabindex': -1
});
}

scope.$watch('isExpanded', function (newValue, oldValue) {
Expand Down
4 changes: 2 additions & 2 deletions dist/v-accordion.min.css

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading

0 comments on commit 49b8d5b

Please sign in to comment.