Merge pull request #8835 from openstreetmap/accessible_ui

Improve UI accessibility - Focus on screen reader support
This commit is contained in:
Martin Raifer
2021-12-07 17:37:56 +01:00
committed by GitHub
40 changed files with 124 additions and 33 deletions
+3
View File
@@ -49,6 +49,7 @@ _Breaking developer changes, which may affect downstream projects or sites that
#### :sparkles: Usability & Accessibility
* Add a preview to colour fields, showing a native colour picker dialog on click ([#8782], thanks [@k-yle])
* Tag keys of a multi-selection can now also be changed in the tags editor when the tag values differ in the selected features. ([#8836])
* Improve screen reader accessibility ([#8835], thanks [@mbrzakovic])
#### :scissors: Operations
* Split operation now indicates more clearly when multiple ways will be affected and gives a hint how to restrict the operation to a single line ([#8818])
* Many operations now better preserve OSM object history ([#8839], thanks [@tpetillon])
@@ -92,10 +93,12 @@ _Breaking developer changes, which may affect downstream projects or sites that
[#8825]: https://github.com/openstreetmap/iD/pull/8825
[#8828]: https://github.com/openstreetmap/iD/pull/8828
[#8831]: https://github.com/openstreetmap/iD/issues/8831
[#8835]: https://github.com/openstreetmap/iD/pull/8835
[#8836]: https://github.com/openstreetmap/iD/issues/8836
[#8839]: https://github.com/openstreetmap/iD/pull/8839
[@k-yle]: https://github.com/k-yle
[@tpetillon]: https://github.com/tpetillon
[@mbrzakovic]: https://github.com/mbrzakovic
# 2.20.2
##### 2021-Oct-28
+9 -3
View File
@@ -81,9 +81,14 @@ h2 {
font-weight: bold;
margin-bottom: 20px;
}
.header h2 {
font-size: 20px;
line-height: 1.25;
font-weight: bold;
margin-bottom: 0px;
}
h3:last-child,
h2:last-child,
h4:last-child { margin-bottom: 0;}
h3 {
@@ -3652,12 +3657,13 @@ li.issue-fix-item button:not(.actionable) .fix-icon {
padding: 10px;
}
.display-control h5 {
.display-options-container label {
padding-bottom: 0;
padding-top: 10px;
}
.display-control h5 span {
.display-options-container label span {
font-weight: bold;
margin: 5px;
}
+17 -1
View File
@@ -12,6 +12,12 @@ en:
list: list
text: text
deselect: deselect
close: close
forward: forward
backward: backward
expand: expand
collapse: collapse
plus: add
toolbar:
inspect: Inspect
undo_redo: Undo / Redo
@@ -704,6 +710,7 @@ en:
features: Features
title_count: "{title} ({count})"
add_to_relation: Add to a relation
add_to_tag: Add a tag
new_relation: New relation...
choose_relation: Choose a parent relation
role: Role
@@ -713,7 +720,7 @@ en:
one: "{n} result for {search}"
other: "{n} results for {search}"
no_documentation_key: "There is no documentation available."
edit_reference: "edit/translate"
edit_reference: "edit/translate documentation"
wiki_reference: View documentation
wiki_en_reference: View documentation in English
key_value: "key=value"
@@ -740,6 +747,8 @@ en:
type: Type
default: Default
layer: Layer
increment: increment
decrement: decrement
add: Add
none: None
node: Node
@@ -765,6 +774,7 @@ en:
network_ref_direction: "{network} {ref} {direction}"
network_ref_from_to: "{network} {ref} from {from} to {to}"
network_ref_from_to_via: "{network} {ref} from {from} to {to} via {via}"
speed_unit: "Speed unit"
roadheight:
# symbol for meters
meter: m
@@ -802,6 +812,12 @@ en:
tooltip: Show coordinates and regional details.
fix_misalignment: Imagery Offset
offset: "Drag anywhere in the gray area below to adjust the imagery offset, or enter the offset values in meters."
offset_label: "Adjust Imagery Offset"
nudge:
top: "nudge top"
left: "nudge left"
right: "nudge right"
bottom: "nudge bottom"
map_data:
title: Map Data
description: Map Data
+1 -1
View File
File diff suppressed because one or more lines are too long
+1
View File
@@ -198,6 +198,7 @@ export function rendererTileLayer(context) {
image.enter()
.append('img')
.attr('class', 'tile')
.attr('alt', '')
.attr('draggable', 'false')
.style('width', _tileSize + 'px')
.style('height', _tileSize + 'px')
+2 -1
View File
@@ -220,12 +220,13 @@ export function uiCommit(context) {
headerTitle
.append('div')
.append('h3')
.append('h2')
.call(t.append('commit.title'));
headerTitle
.append('button')
.attr('class', 'close')
.attr('title', t('icons.close'))
.on('click', function() {
dispatch.call('cancel', this);
})
+2 -1
View File
@@ -60,11 +60,12 @@ export function uiConflicts(context) {
headerEnter
.append('button')
.attr('class', 'fr')
.attr('title', t('icons.close'))
.on('click', cancel)
.call(svgIcon('#iD-icon-close'));
headerEnter
.append('h3')
.append('h2')
.call(t.append('save.conflict.header'));
var bodyEnter = selection.selectAll('.body')
+2 -1
View File
@@ -26,13 +26,14 @@ export function uiDataEditor(context) {
headerEnter
.append('button')
.attr('class', 'close')
.attr('title', t('icons.close'))
.on('click', function() {
context.enter(modeBrowse(context));
})
.call(svgIcon('#iD-icon-close'));
headerEnter
.append('h3')
.append('h2')
.call(t.append('map_data.title'));
+8 -2
View File
@@ -5,7 +5,7 @@ import { svgIcon } from '../svg/icon';
import { utilFunctor } from '../util';
import { utilRebind } from '../util/rebind';
import { uiToggle } from './toggle';
import { localizer } from '../core/localizer';
import { t, localizer } from '../core/localizer';
export function uiDisclosure(context, key, expandedDefault) {
@@ -30,7 +30,9 @@ export function uiDisclosure(context, key, expandedDefault) {
// enter
var hideToggleEnter = hideToggle.enter()
.append('h3')
.append('a')
.attr('role', 'button')
.attr('href', '#')
.attr('class', 'hide-toggle hide-toggle-' + key)
.call(svgIcon('', 'pre-text', 'hide-toggle-icon'));
@@ -45,6 +47,8 @@ export function uiDisclosure(context, key, expandedDefault) {
hideToggle
.on('click', toggle)
.attr('title', t(`icons.${_expanded ? 'collapse' : 'expand'}`))
.attr('aria-expanded', _expanded)
.classed('expanded', _expanded);
hideToggle.selectAll('.hide-toggle-text')
@@ -82,7 +86,9 @@ export function uiDisclosure(context, key, expandedDefault) {
}
hideToggle
.classed('expanded', _expanded);
.classed('expanded', _expanded)
.attr('aria-expanded', _expanded)
.attr('title', t(`icons.${_expanded ? 'collapse' : 'expand'}`));
hideToggle.selectAll('.hide-toggle-icon')
.attr('xlink:href', _expanded ? '#iD-icon-down'
+7 -3
View File
@@ -42,25 +42,29 @@ export function uiEntityEditor(context) {
.append('div')
.attr('class', 'header fillL');
var direction = (localizer.textDirection() === 'rtl') ? 'forward' : 'backward';
headerEnter
.append('button')
.attr('class', 'preset-reset preset-choose')
.call(svgIcon((localizer.textDirection() === 'rtl') ? '#iD-icon-forward' : '#iD-icon-backward'));
.attr('title', t(`icons.${direction}`))
.call(svgIcon(`#iD-icon-${direction}`));
headerEnter
.append('button')
.attr('class', 'close')
.attr('title', t('icons.close'))
.on('click', function() { context.enter(modeBrowse(context)); })
.call(svgIcon(_modified ? '#iD-icon-apply' : '#iD-icon-close'));
headerEnter
.append('h3');
.append('h2');
// Update
header = header
.merge(headerEnter);
header.selectAll('h3')
header.selectAll('h2')
.html(_entityIDs.length === 1 ? t.html('inspector.edit') : t.html('inspector.edit_features'));
header.selectAll('.preset-reset')
+1 -1
View File
@@ -33,7 +33,7 @@ export function uiFeatureList(context) {
.attr('class', 'header fillL');
header
.append('h3')
.append('h2')
.call(t.append('inspector.feature_list'));
var searchWrap = selection
+1
View File
@@ -201,6 +201,7 @@ export function uiFieldHelp(context, fieldName) {
titleEnter
.append('button')
.attr('class', 'fr close')
.attr('title', t('icons.close'))
.on('click', function(d3_event) {
d3_event.stopPropagation();
d3_event.preventDefault();
+4
View File
@@ -110,6 +110,10 @@ export function uiFieldText(field, context) {
var which = (d > 0 ? 'increment' : 'decrement');
return 'form-field-button ' + which;
})
.attr('title', function(d){
var which = (d > 0 ? 'increment' : 'decrement');
return t(`inspector.${which}`);
})
.merge(buttons)
.on('click', function(d3_event, d) {
d3_event.preventDefault();
+2
View File
@@ -185,6 +185,7 @@ export function uiFieldLocalized(field, context) {
translateButton = translateButton.enter()
.append('button')
.attr('class', 'localized-add form-field-button')
.attr('aria-label', t('icons.plus'))
.call(svgIcon('#iD-icon-plus'))
.merge(translateButton);
@@ -382,6 +383,7 @@ export function uiFieldLocalized(field, context) {
label
.append('button')
.attr('class', 'remove-icon-multilingual')
.attr('title', t('icons.remove'))
.on('click', function(d3_event, d) {
if (field.locked()) return;
d3_event.preventDefault();
+1
View File
@@ -60,6 +60,7 @@ export function uiFieldRoadspeed(field, context) {
.append('input')
.attr('type', 'text')
.attr('class', 'roadspeed-unit')
.attr('aria-label', t('inspector.speed_unit'))
.call(unitCombo)
.merge(unitInput);
+2
View File
@@ -77,6 +77,7 @@ export function uiGeolocate(context) {
function updateButtonState() {
_button.classed('active', _layer.enabled());
_button.attr('aria-pressed', _layer.enabled());
}
return function(selection) {
@@ -85,6 +86,7 @@ export function uiGeolocate(context) {
_button = selection
.append('button')
.on('click', click)
.attr('aria-pressed', false)
.call(svgIcon('#iD-icon-geolocate', 'light'))
.call(uiTooltip()
.placement((localizer.textDirection() === 'rtl') ? 'right' : 'left')
+2 -1
View File
@@ -31,11 +31,12 @@ export function uiImproveOsmEditor(context) {
headerEnter
.append('button')
.attr('class', 'close')
.attr('title', t('icons.close'))
.on('click', () => context.enter(modeBrowse(context)))
.call(svgIcon('#iD-icon-close'));
headerEnter
.append('h3')
.append('h2')
.call(t.append('QA.improveOSM.title'));
let body = selection.selectAll('.body')
+1
View File
@@ -63,6 +63,7 @@ export function uiInfo(context) {
title
.append('button')
.attr('class', 'close')
.attr('title', t('icons.close'))
.on('click', function(d3_event, d) {
d3_event.stopImmediatePropagation();
d3_event.preventDefault();
+2
View File
@@ -320,6 +320,7 @@ export function uiInit(context) {
.append('a')
.attr('target', '_blank')
.attr('href', 'https://github.com/openstreetmap/iD/issues')
.attr('aria-label', t('report_a_bug'))
.call(svgIcon('#iD-icon-bug', 'light'))
.call(uiTooltip().title(t.html('report_a_bug')).placement('top'));
@@ -327,6 +328,7 @@ export function uiInit(context) {
.append('a')
.attr('target', '_blank')
.attr('href', 'https://github.com/openstreetmap/iD/blob/develop/CONTRIBUTING.md#translating')
.attr('aria-label', t('help_translate'))
.call(svgIcon('#iD-icon-translate', 'light'))
.call(uiTooltip().title(t.html('help_translate')).placement('top'));
+2 -1
View File
@@ -30,11 +30,12 @@ export function uiKeepRightEditor(context) {
headerEnter
.append('button')
.attr('class', 'close')
.attr('title', t('icons.close'))
.on('click', () => context.enter(modeBrowse(context)))
.call(svgIcon('#iD-icon-close'));
headerEnter
.append('h3')
.append('h2')
.call(t.append('QA.keepRight.title'));
+2
View File
@@ -1,5 +1,6 @@
import { select as d3_select } from 'd3-selection';
import { t } from './../core/localizer';
import { svgIcon } from '../svg/icon';
import { utilKeybinding } from '../util';
@@ -55,6 +56,7 @@ export function uiModal(selection, blocking) {
modal
.append('button')
.attr('class', 'close')
.attr('title', t('icons.close'))
.on('click', shaded.close)
.call(svgIcon('#iD-icon-close'));
+2 -1
View File
@@ -46,13 +46,14 @@ export function uiNoteEditor(context) {
headerEnter
.append('button')
.attr('class', 'close')
.attr('title', t('icons.close'))
.on('click', function() {
context.enter(modeBrowse(context));
})
.call(svgIcon('#iD-icon-close'));
headerEnter
.append('h3')
.append('h2')
.call(t.append('note.title'));
+1
View File
@@ -42,6 +42,7 @@ export function uiNoteHeader() {
iconEnter
.append('div')
.attr('class', 'note-icon-annotation')
.attr('title', t('icons.close'))
.call(svgIcon(statusIcon, 'icon-annotation'));
});
+2 -1
View File
@@ -30,11 +30,12 @@ export function uiOsmoseEditor(context) {
headerEnter
.append('button')
.attr('class', 'close')
.attr('title', t('icons.close'))
.on('click', () => context.enter(modeBrowse(context)))
.call(svgIcon('#iD-icon-close'));
headerEnter
.append('h3')
.append('h2')
.call(t.append('QA.osmose.title'));
let body = selection.selectAll('.body')
+2 -1
View File
@@ -3,7 +3,7 @@ import {
} from 'd3-selection';
import { svgIcon } from '../svg/icon';
import { localizer } from '../core/localizer';
import { t, localizer } from '../core/localizer';
import { uiTooltip } from './tooltip';
@@ -110,6 +110,7 @@ export function uiPane(id, context) {
heading
.append('button')
.attr('title', t('icons.close'))
.on('click', hidePane)
.call(svgIcon('#iD-icon-close'));
+1
View File
@@ -361,6 +361,7 @@ export function uiPaneHelp(context) {
.enter()
.append('li')
.append('a')
.attr('role', 'button')
.attr('href', '#')
.html(function(d) { return d.title; })
.on('click', function(d3_event, d) {
+2
View File
@@ -2,6 +2,7 @@ import {
select as d3_select
} from 'd3-selection';
import { t } from '../core/localizer';
import { dispatch as d3_dispatch } from 'd3-dispatch';
import { svgIcon } from '../svg/icon';
import { utilGetDimensions } from '../util/dimensions';
@@ -18,6 +19,7 @@ export function uiPhotoviewer(context) {
selection
.append('button')
.attr('class', 'thumb-hide')
.attr('title', t('icons.close'))
.on('click', function () {
if (services.streetside) { services.streetside.hideViewer(context); }
if (services.mapillary) { services.mapillary.hideViewer(context); }
+8 -3
View File
@@ -34,14 +34,17 @@ export function uiPresetList(context) {
.attr('class', 'header fillL');
var message = messagewrap
.append('h3')
.append('h2')
.call(t.append('inspector.choose'));
var direction = (localizer.textDirection() === 'rtl') ? 'backward' : 'forward';
messagewrap
.append('button')
.attr('class', 'preset-choose')
.attr('title', direction)
.on('click', function() { dispatch.call('cancel', this); })
.call(svgIcon((localizer.textDirection() === 'rtl') ? '#iD-icon-backward' : '#iD-icon-forward'));
.call(svgIcon(`#iD-icon-${direction}`));
function initialKeydown(d3_event) {
// hack to let delete shortcut work when search is autofocused
@@ -273,7 +276,8 @@ export function uiPresetList(context) {
var iconName = isExpanded ?
(localizer.textDirection() === 'rtl' ? '#iD-icon-backward' : '#iD-icon-forward') : '#iD-icon-down';
d3_select(this)
.classed('expanded', !isExpanded);
.classed('expanded', !isExpanded)
.attr('title', !isExpanded ? t('icons.collapse') : t('icons.expand'));
d3_select(this).selectAll('div.label-inner svg.icon use')
.attr('href', iconName);
item.choose();
@@ -284,6 +288,7 @@ export function uiPresetList(context) {
var button = wrap
.append('button')
.attr('class', 'preset-list-button')
.attr('title', t('icons.expand'))
.classed('expanded', false)
.call(uiPresetIcon()
.geometry(geometries.length === 1 && geometries[0])
@@ -60,11 +60,10 @@ export function uiSectionBackgroundDisplayOptions(context) {
var slidersEnter = containerEnter.selectAll('.display-control')
.data(_sliders)
.enter()
.append('div')
.append('label')
.attr('class', function(d) { return 'display-control display-control-' + d; });
slidersEnter
.append('h5')
.html(function(d) { return t.html('background.' + d); })
.append('span')
.attr('class', function(d) { return 'display-option-value display-option-value-' + d; });
@@ -90,7 +89,7 @@ export function uiSectionBackgroundDisplayOptions(context) {
sildersControlEnter
.append('button')
.attr('title', t('background.reset'))
.attr('title', function(d) { return `${t('background.reset')} ${t('background.' + d)}`; })
.attr('class', function(d) { return 'display-option-reset display-option-reset-' + d; })
.on('click', function(d3_event, d) {
if (d3_event.button !== 0) return;
@@ -102,6 +101,7 @@ export function uiSectionBackgroundDisplayOptions(context) {
containerEnter
.append('a')
.attr('class', 'display-option-resetlink')
.attr('role', 'button')
.attr('href', '#')
.call(t.append('background.reset_all'))
.on('click', function(d3_event) {
+2
View File
@@ -148,6 +148,7 @@ export function uiSectionBackgroundOffset(context) {
.attr('class', 'nudge-inner-rect')
.append('input')
.attr('type', 'text')
.attr('aria-label', t('background.offset_label'))
.on('change', inputOffset);
nudgeWrapEnter
@@ -155,6 +156,7 @@ export function uiSectionBackgroundOffset(context) {
.selectAll('button')
.data(_directions).enter()
.append('button')
.attr('title', function(d) { return t(`background.nudge.${d[0]}`); })
.attr('class', function(d) { return d[0] + ' nudge'; })
.on('click', function(d3_event, d) {
nudge(d[1]);
+2
View File
@@ -31,6 +31,7 @@ export function uiSectionMapFeatures(context) {
footer
.append('a')
.attr('class', 'feature-list-link')
.attr('role', 'button')
.attr('href', '#')
.call(t.append('issues.disable_all'))
.on('click', function(d3_event) {
@@ -41,6 +42,7 @@ export function uiSectionMapFeatures(context) {
footer
.append('a')
.attr('class', 'feature-list-link')
.attr('role', 'button')
.attr('href', '#')
.call(t.append('issues.enable_all'))
.on('click', function(d3_event) {
+4 -1
View File
@@ -354,6 +354,7 @@ export function uiSectionRawMembershipEditor(context) {
labelEnter
.append('button')
.attr('class', 'remove member-delete')
.attr('title', t('icons.remove'))
.call(svgIcon('#iD-operation-delete'))
.on('click', deleteMembership);
@@ -421,6 +422,7 @@ export function uiSectionRawMembershipEditor(context) {
newLabelEnter
.append('button')
.attr('class', 'remove member-delete')
.attr('title', t('icons.remove'))
.call(svgIcon('#iD-operation-delete'))
.on('click', function() {
list.selectAll('.member-row-new')
@@ -461,7 +463,8 @@ export function uiSectionRawMembershipEditor(context) {
var addRelationButton = addRowEnter
.append('button')
.attr('class', 'add-relation');
.attr('class', 'add-relation')
.attr('aria-label', t('inspector.add_to_relation'));
addRelationButton
.call(svgIcon('#iD-icon-plus', 'light'));
+10 -3
View File
@@ -7,9 +7,10 @@ import { uiCombobox } from '../combobox';
import { uiSection } from '../section';
import { uiTagReference } from '../tag_reference';
import { prefs } from '../../core/preferences';
import { t } from '../../core/localizer';
import { localizer, t } from '../../core/localizer';
import { utilArrayDifference, utilArrayIdentical } from '../../util/array';
import { utilGetSetValue, utilNoAuto, utilRebind, utilTagDiff } from '../../util';
import { uiTooltip } from '..';
export function uiSectionRawTagEditor(id, context) {
@@ -84,7 +85,8 @@ export function uiSectionRawTagEditor(id, context) {
var optionsEnter = options.enter()
.insert('div', ':first-child')
.attr('class', 'raw-tag-options');
.attr('class', 'raw-tag-options')
.attr('role', 'tablist');
var optionEnter = optionsEnter.selectAll('.raw-tag-option')
.data(availableViews, function(d) { return d.id; })
@@ -95,13 +97,16 @@ export function uiSectionRawTagEditor(id, context) {
.attr('class', function(d) {
return 'raw-tag-option raw-tag-option-' + d.id + (_tagView === d.id ? ' selected' : '');
})
.attr('aria-selected', function(d) { return _tagView === d.id; })
.attr('role', 'tab')
.attr('title', function(d) { return t('icons.' + d.id); })
.on('click', function(d3_event, d) {
_tagView = d.id;
prefs('raw-tag-editor-view', d.id);
wrap.selectAll('.raw-tag-option')
.classed('selected', function(datum) { return datum === d; });
.classed('selected', function(datum) { return datum === d; })
.attr('aria-selected', function(datum) { return datum === d; });
wrap.selectAll('.tag-text')
.classed('hide', (d.id !== 'text'))
@@ -158,7 +163,9 @@ export function uiSectionRawTagEditor(id, context) {
addRowEnter
.append('button')
.attr('class', 'add-tag')
.attr('aria-label', t('inspector.add_to_tag'))
.call(svgIcon('#iD-icon-plus', 'light'))
.call(uiTooltip().title(t.html('inspector.add_to_tag')).placement(localizer.textDirection() === 'ltr' ? 'right' : 'left'))
.on('click', addTag);
addRowEnter
+2
View File
@@ -44,6 +44,7 @@ export function uiSectionValidationRules(context) {
ruleLinks
.append('a')
.attr('class', 'issue-rules-link')
.attr('role', 'button')
.attr('href', '#')
.call(t.append('issues.disable_all'))
.on('click', function(d3_event) {
@@ -54,6 +55,7 @@ export function uiSectionValidationRules(context) {
ruleLinks
.append('a')
.attr('class', 'issue-rules-link')
.attr('role', 'button')
.attr('href', '#')
.call(t.append('issues.enable_all'))
.on('click', function(d3_event) {
+2 -2
View File
@@ -25,8 +25,8 @@ export function uiShortcuts(context) {
content
.append('div')
.attr('class', 'modal-section')
.append('h3')
.attr('class', 'modal-section header')
.append('h2')
.call(t.append('shortcuts.title'));
fileFetcher.get('shortcuts')
+2 -1
View File
@@ -81,12 +81,13 @@ export function uiSuccess(context) {
.attr('class', 'header fillL');
header
.append('h3')
.append('h2')
.call(t.append('success.just_edited'));
header
.append('button')
.attr('class', 'close')
.attr('title', t('icons.close'))
.on('click', () => dispatch.call('cancel'))
.call(svgIcon('#iD-icon-close'));
+1
View File
@@ -53,6 +53,7 @@ export function uiTagReference(what) {
_body
.append('img')
.attr('class', 'tag-reference-wiki-image')
.attr('alt', docs.description)
.attr('src', docs.imageURL)
.on('load', function() { done(); })
.on('error', function() { d3_select(this).remove(); done(); });
+2
View File
@@ -137,7 +137,9 @@ export function uiToolOldDrawModes(context) {
// update
buttons = buttons
.merge(buttonsEnter)
.attr('aria-disabled', function(d) { return !enabled(d); })
.classed('disabled', function(d) { return !enabled(d); })
.attr('aria-pressed', function(d) { return context.mode() && context.mode().button === d.button; })
.classed('active', function(d) { return context.mode() && context.mode().button === d.button; });
}
};
+3 -1
View File
@@ -108,7 +108,9 @@ export function uiToolNotes(context) {
buttons = buttons
.merge(buttonsEnter)
.classed('disabled', function(d) { return !enabled(d); })
.classed('active', function(d) { return context.mode() && context.mode().button === d.button; });
.attr('aria-disabled', function(d) { return !enabled(d); })
.classed('active', function(d) { return context.mode() && context.mode().button === d.button; })
.attr('aria-pressed', function(d) { return context.mode() && context.mode().button === d.button; });
}
};
+1
View File
@@ -13,6 +13,7 @@ export function uiToolSidebarToggle(context) {
selection
.append('button')
.attr('class', 'bar-button')
.attr('aria-label', t('sidebar.tooltip'))
.on('click', function() {
context.ui().sidebar.toggle();
})