UNPKG

highcharts

Version:
271 lines (239 loc) 9.05 kB
/* * * * (c) 2009-2019 Øystein Moseng * * Accessibility component for the range selector. * * License: www.highcharts.com/license * * */ 'use strict'; import H from '../../../parts/Globals.js'; import AccessibilityComponent from '../AccessibilityComponent.js'; import KeyboardNavigationHandler from '../KeyboardNavigationHandler.js'; /** * Highlight range selector button by index. * * @private * @function Highcharts.Chart#highlightRangeSelectorButton * * @param {number} ix * * @return {boolean} */ H.Chart.prototype.highlightRangeSelectorButton = function (ix) { var buttons = this.rangeSelector.buttons; // Deselect old if (buttons[this.highlightedRangeSelectorItemIx]) { buttons[this.highlightedRangeSelectorItemIx].setState( this.oldRangeSelectorItemState || 0 ); } // Select new this.highlightedRangeSelectorItemIx = ix; if (buttons[ix]) { this.setFocusToElement(buttons[ix].box, buttons[ix].element); this.oldRangeSelectorItemState = buttons[ix].state; buttons[ix].setState(2); return true; } return false; }; /** * The RangeSelectorComponent class * * @private * @class * @name Highcharts.RangeSelectorComponent * @param {Highcharts.Chart} chart * Chart object */ var RangeSelectorComponent = function (chart) { this.initBase(chart); }; RangeSelectorComponent.prototype = new AccessibilityComponent(); H.extend(RangeSelectorComponent.prototype, /** @lends Highcharts.RangeSelectorComponent */ { // eslint-disable-line /** * Called on first render/updates to the chart, including options changes. */ onChartUpdate: function () { var chart = this.chart, component = this, rangeSelector = chart.rangeSelector; if (!rangeSelector) { return; } // Make sure buttons are accessible and focusable if (rangeSelector.buttons && rangeSelector.buttons.length) { rangeSelector.buttons.forEach(function (button) { component.unhideElementFromScreenReaders(button.element); button.element.setAttribute('tabindex', '-1'); button.element.setAttribute('role', 'button'); button.element.setAttribute( 'aria-label', chart.langFormat( 'accessibility.rangeSelectorButton', { chart: chart, buttonText: button.text && button.text.textStr } ) ); }); } // Make sure input boxes are accessible and focusable if (rangeSelector.maxInput && rangeSelector.minInput) { ['minInput', 'maxInput'].forEach(function (key, i) { if (rangeSelector[key]) { component.unhideElementFromScreenReaders( rangeSelector[key] ); rangeSelector[key].setAttribute('tabindex', '-1'); rangeSelector[key].setAttribute('role', 'textbox'); rangeSelector[key].setAttribute( 'aria-label', chart.langFormat( 'accessibility.rangeSelector' + (i ? 'MaxInput' : 'MinInput'), { chart: chart } ) ); } }); } }, /** * Get navigation for the range selector buttons. * @private * @return {Highcharts.KeyboardNavigationHandler} The module object. */ getRangeSelectorButtonNavigation: function () { var chart = this.chart, keys = this.keyCodes, a11yOptions = chart.options.accessibility, component = this; return new KeyboardNavigationHandler(chart, { keyCodeMap: [ // Left/Right/Up/Down [[ keys.left, keys.right, keys.up, keys.down ], function (keyCode) { var direction = ( keyCode === keys.left || keyCode === keys.up ) ? -1 : 1; // Try to highlight next/prev button if ( !chart.highlightRangeSelectorButton( chart.highlightedRangeSelectorItemIx + direction ) ) { // If we failed, handle wrap around/move if (a11yOptions.keyboardNavigation.wrapAround) { this.init(direction); return this.response.success; } return this.response[direction > 0 ? 'next' : 'prev']; } }], // Enter/Spacebar [[ keys.enter, keys.space ], function () { // Don't allow click if button used to be disabled if (chart.oldRangeSelectorItemState !== 3) { component.fakeClickEvent( chart.rangeSelector.buttons[ chart.highlightedRangeSelectorItemIx ].element ); } }] ], // Only run this module if we have range selector validate: function () { return chart.rangeSelector && chart.rangeSelector.buttons && chart.rangeSelector.buttons.length; }, // Focus first/last button init: function (direction) { chart.highlightRangeSelectorButton( direction > 0 ? 0 : chart.rangeSelector.buttons.length - 1 ); } }); }, /** * Get navigation for the range selector input boxes. * @private * @return {Highcharts.KeyboardNavigationHandler} * The module object. */ getRangeSelectorInputNavigation: function () { var chart = this.chart, keys = this.keyCodes; return new KeyboardNavigationHandler(chart, { keyCodeMap: [ // Tab/Up/Down [[ keys.tab, keys.up, keys.down ], function (keyCode, e) { var direction = ( keyCode === keys.tab && e.shiftKey || keyCode === keys.up ) ? -1 : 1, newIx = chart.highlightedInputRangeIx = chart.highlightedInputRangeIx + direction; // Try to highlight next/prev item in list. if (newIx > 1 || newIx < 0) { // Out of range return this.response[direction > 0 ? 'next' : 'prev']; } chart.rangeSelector[ newIx ? 'maxInput' : 'minInput' ].focus(); return this.response.success; }] ], // Only run if we have range selector with input boxes validate: function () { var inputVisible = ( chart.rangeSelector && chart.rangeSelector.inputGroup && chart.rangeSelector.inputGroup.element .getAttribute('visibility') !== 'hidden' ); return ( inputVisible && chart.options.rangeSelector.inputEnabled !== false && chart.rangeSelector.minInput && chart.rangeSelector.maxInput ); }, // Highlight first/last input box init: function (direction) { chart.highlightedInputRangeIx = direction > 0 ? 0 : 1; chart.rangeSelector[ chart.highlightedInputRangeIx ? 'maxInput' : 'minInput' ].focus(); }, // Hide HTML element when leaving boxes terminate: function () { var rangeSel = chart.rangeSelector; if (rangeSel && rangeSel.maxInput && rangeSel.minInput) { rangeSel.hideInput('max'); rangeSel.hideInput('min'); } } }); }, /** * Get keyboard navigation handlers for this component. * @return {Array<Highcharts.KeyboardNavigationHandler>} * List of module objects. */ getKeyboardNavigation: function () { return [ this.getRangeSelectorButtonNavigation(), this.getRangeSelectorInputNavigation() ]; } }); export default RangeSelectorComponent;