Skip to content

Commit

Permalink
Make filter pills keyboard accessible (elastic#13331)
Browse files Browse the repository at this point in the history
Fixes elastic#12639

It's now possible to tab to each filter's actions and interact with them via the keyboard.

In order to get the actions to show/hide on both mouse hover and action focus I had to create a new filter-pill component that could manage a bit of state to track whether the user was interacting with a given pill or not.
  • Loading branch information
Bargs authored Aug 8, 2017
1 parent 9aed822 commit 542d553
Show file tree
Hide file tree
Showing 6 changed files with 135 additions and 51 deletions.
40 changes: 8 additions & 32 deletions src/ui/public/filter_bar/filter_bar.html
Original file line number Diff line number Diff line change
Expand Up @@ -26,39 +26,15 @@
class="filter-bar"
ng-show="filters.length || showAddFilterButton()"
>
<div
class="filter"
ng-class="{ negate: filter.meta.negate, disabled: filter.meta.disabled }"
<filter-pill
ng-repeat="filter in filters track by $index"
data-test-subj="filter filter-{{ filter.meta.disabled ? 'disabled' : 'enabled' }} {{ filter.meta.key ? 'filter-key-' + filter.meta.key : '' }} {{ filter.meta.value ? 'filter-value-' + filter.meta.value : '' }}"
>
<div class="filter-description">
<span ng-if="filter.$state.store == 'globalState'"><i class="fa fa-fw fa-thumb-tack pinned"></i></span>
<span ng-if="filter.meta.alias">{{ filter.meta.alias }}</span>
<span ng-if="!filter.meta.alias">{{ filter.meta.key }}:</span>
<span ng-if="!filter.meta.alias">"{{ filter.meta.value }}"</span>
</div>
<div class="filter-actions">
<a class="action filter-toggle" ng-click="toggleFilter(filter)" data-test-subj="disableFilter-{{ filter.meta.key }}">
<i ng-show="filter.meta.disabled" class="fa fa-fw fa-square-o disabled"></i>
<i ng-hide="filter.meta.disabled" class="fa fa-fw fa-check-square-o enabled"></i>
</a>
<a class="action filter-pin" ng-click="pinFilter(filter)" data-test-subj="pinFilter-{{ filter.meta.key }}">
<i ng-show="filter.$state.store == 'globalState'" class="fa fa-fw fa-thumb-tack pinned"></i>
<i ng-hide="filter.$state.store == 'globalState'" class="fa fa-fw fa-thumb-tack fa-rotate-270 unpinned"></i>
</a>
<a class="action filter-invert" ng-click="invertFilter(filter)" data-test-subj="invertFilter-{{ filter.meta.key }}">
<i ng-show="filter.meta.negate" class="fa fa-fw fa-search-plus negative"></i>
<i ng-hide="filter.meta.negate" class="fa fa-fw fa-search-minus positive"></i>
</a>
<a class="action filter-remove" ng-click="deleteFilter(filter)">
<i class="fa fa-fw fa-trash" data-test-subj="removeFilter-{{ filter.meta.key }}"></i>
</a>
<a class="action filter-edit" ng-click="editFilter(filter)">
<i class="fa fa-fw fa-edit"></i>
</a>
</div>
</div>
filter="filter"
on-toggle-filter="toggleFilter"
on-pin-filter="pinFilter"
on-invert-filter="invertFilter"
on-delete-filter="deleteFilter"
on-edit-filter="editFilter"
></filter-pill>

<div
class="filter-link"
Expand Down
1 change: 1 addition & 0 deletions src/ui/public/filter_bar/filter_bar.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import _ from 'lodash';
import template from 'ui/filter_bar/filter_bar.html';
import 'ui/directives/json_input';
import '../filter_editor';
import './filter_pill/filter_pill';
import { filterAppliedAndUnwrap } from 'ui/filter_bar/lib/filter_applied_and_unwrap';
import { FilterBarLibMapAndFlattenFiltersProvider } from 'ui/filter_bar/lib/map_and_flatten_filters';
import { FilterBarLibMapFlattenAndWrapFiltersProvider } from 'ui/filter_bar/lib/map_flatten_and_wrap_filters';
Expand Down
36 changes: 18 additions & 18 deletions src/ui/public/filter_bar/filter_bar.less
Original file line number Diff line number Diff line change
Expand Up @@ -94,35 +94,25 @@ filter-bar {
padding: 4px 8px;
border-radius: 12px;


&:hover {
> .filter-actions {
display: block;
}

> .filter-description {
opacity: 0.15;
background: transparent;
overflow: hidden;
}
}

> .filter-actions {
.filter-actions {
font-size: 1.1em;
line-height: 1.4em;
position: absolute;
padding: 4px 8px;
top: 0;
left: 0;
width: 100%;
display: none;
opacity: 0;
text-align: center;
white-space: nowrap;
display: flex;

> * {
.action {
border: none;
border-right: 1px solid rgba(255, 255, 255, 0.4);
padding-right: 0;
margin-right: 5px;
padding: 0;
background-color: transparent;
flex: 1 1 auto;

&:last-child {
border-right: 0;
Expand All @@ -136,6 +126,16 @@ filter-bar {
}
}

.filter-actions-activated {
opacity: 1;
}

.filter-description-deactivated {
opacity: 0.15;
background: transparent;
overflow: hidden;
}

&.negate {
background-color: @filter-bar-bar-filter-negate-bg;
}
Expand Down
74 changes: 74 additions & 0 deletions src/ui/public/filter_bar/filter_pill/filter_pill.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
<div
class="filter"
ng-class="{ negate: pill.filter.meta.negate, disabled: pill.filter.meta.disabled }"
data-test-subj="filter filter-{{ pill.filter.meta.disabled ? 'disabled' : 'enabled' }} {{ pill.filter.meta.key ? 'filter-key-' + pill.filter.meta.key : '' }} {{ pill.filter.meta.value ? 'filter-value-' + pill.filter.meta.value : '' }}"
ng-mouseover="pill.activateActions()"
ng-mouseleave="pill.deactivateActions()"
>

<div class="filter-description" ng-class="{'filter-description-deactivated': pill.areActionsActivated}">
<span ng-if="pill.filter.$state.store == 'globalState'"><i class="fa fa-fw fa-thumb-tack pinned"></i></span>
<span ng-if="pill.filter.meta.alias">{{ pill.filter.meta.alias }}</span>
<span ng-if="!pill.filter.meta.alias">{{ pill.filter.meta.key }}:</span>
<span ng-if="!pill.filter.meta.alias">"{{ pill.filter.meta.value }}"</span>
</div>

<div class="filter-actions" ng-class="{'filter-actions-activated': pill.areActionsActivated}">
<button
class="action filter-toggle"
ng-click="pill.onToggleFilter(pill.filter)"
data-test-subj="disableFilter-{{ pill.filter.meta.key }}"
ng-focus="pill.activateActions()"
ng-blur="pill.deactivateActions()"
aria-label="{{pill.filter.meta.disabled ? 'Enable filter' : 'Disable filter'}}"
>
<i ng-show="pill.filter.meta.disabled" class="fa fa-fw fa-square-o disabled"></i>
<i ng-hide="pill.filter.meta.disabled" class="fa fa-fw fa-check-square-o enabled"></i>
</button>

<button
class="action filter-pin"
ng-click="pill.onPinFilter(pill.filter)"
data-test-subj="pinFilter-{{ pill.filter.meta.key }}"
ng-focus="pill.activateActions()"
ng-blur="pill.deactivateActions()"
aria-label="{{pill.filter.$state.store == 'globalState' ? 'Unpin filter' : 'Pin filter'}}"
>
<i ng-show="pill.filter.$state.store == 'globalState'" class="fa fa-fw fa-thumb-tack pinned"></i>
<i ng-hide="pill.filter.$state.store == 'globalState'" class="fa fa-fw fa-thumb-tack fa-rotate-270 unpinned"></i>
</button>

<button
class="action filter-invert"
ng-click="pill.onInvertFilter(pill.filter)"
data-test-subj="invertFilter-{{ pill.filter.meta.key }}"
ng-focus="pill.activateActions()"
ng-blur="pill.deactivateActions()"
aria-label="Invert filter"
>
<i ng-show="pill.filter.meta.negate" class="fa fa-fw fa-search-plus negative"></i>
<i ng-hide="pill.filter.meta.negate" class="fa fa-fw fa-search-minus positive"></i>
</button>

<button
class="action filter-remove"
ng-click="pill.onDeleteFilter(pill.filter)"
ng-focus="pill.activateActions()"
ng-blur="pill.deactivateActions()"
aria-label="Remove filter"
>
<i class="fa fa-fw fa-trash" data-test-subj="removeFilter-{{ pill.filter.meta.key }}"></i>
</button>

<button
class="action filter-edit"
ng-click="pill.onEditFilter(pill.filter)"
ng-focus="pill.activateActions()"
ng-blur="pill.deactivateActions()"
aria-label="Edit filter"
>
<i class="fa fa-fw fa-edit"></i>
</button>

</div>
</div>
33 changes: 33 additions & 0 deletions src/ui/public/filter_bar/filter_pill/filter_pill.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import template from './filter_pill.html';
import { uiModules } from 'ui/modules';

const module = uiModules.get('kibana');

module.directive('filterPill', function () {
return {
template,
restrict: 'E',
scope: {
filter: '=',
onToggleFilter: '=',
onPinFilter: '=',
onInvertFilter: '=',
onDeleteFilter: '=',
onEditFilter: '=',
},
bindToController: true,
controllerAs: 'pill',
controller: function filterPillController() {

this.activateActions = () => {
this.areActionsActivated = true;
};

this.deactivateActions = () => {
this.areActionsActivated = false;
};

}
};
});

2 changes: 1 addition & 1 deletion test/functional/page_objects/dashboard_page.js
Original file line number Diff line number Diff line change
Expand Up @@ -429,7 +429,7 @@ export function DashboardPageProvider({ getService, getPageObjects }) {
}

async getFilters(timeout = defaultFindTimeout) {
return await find.allByCssSelector('.filter-bar > .filter', timeout);
return await find.allByCssSelector('.filter-bar .filter', timeout);
}

async getFilterDescriptions(timeout = defaultFindTimeout) {
Expand Down

0 comments on commit 542d553

Please sign in to comment.