mirror of
https://github.com/elastic/kibana.git
synced 2025-04-24 09:48:58 -04:00
Create keyboard mode for ui-ace editor (#13339)
* Add kbn-ui-ace-keyboard-mode directive * Implemented PR feedback * Fix broken tests
This commit is contained in:
parent
12142da71b
commit
e66c1d2ac4
7 changed files with 167 additions and 0 deletions
|
@ -130,6 +130,7 @@
|
|||
|
||||
<div
|
||||
ng-if="field.type === 'json' || field.type === 'array'"
|
||||
kbn-ui-ace-keyboard-mode
|
||||
ui-ace="{ onLoad: aceLoaded, mode: 'json' }"
|
||||
id="{{field.name}}"
|
||||
ng-model="field.value"
|
||||
|
|
|
@ -5,6 +5,7 @@ import { savedObjectManagementRegistry } from 'plugins/kibana/management/saved_o
|
|||
import objectViewHTML from 'plugins/kibana/management/sections/objects/_view.html';
|
||||
import uiRoutes from 'ui/routes';
|
||||
import { uiModules } from 'ui/modules';
|
||||
import 'ui/accessibility/kbn_ui_ace_keyboard_mode';
|
||||
import { castEsToKbnFieldTypeName } from '../../../../../../utils';
|
||||
import { SavedObjectsClientProvider } from 'ui/saved_objects';
|
||||
|
||||
|
|
|
@ -0,0 +1,57 @@
|
|||
import angular from 'angular';
|
||||
import sinon from 'sinon';
|
||||
import expect from 'expect.js';
|
||||
import ngMock from 'ng_mock';
|
||||
import '../kbn_ui_ace_keyboard_mode';
|
||||
import {
|
||||
ENTER_KEY,
|
||||
ESC_KEY_CODE,
|
||||
} from 'ui_framework/services';
|
||||
|
||||
describe('kbnUiAceKeyboardMode directive', () => {
|
||||
let element;
|
||||
|
||||
beforeEach(ngMock.module('kibana'));
|
||||
|
||||
beforeEach(ngMock.inject(($compile, $rootScope) => {
|
||||
element = $compile(`<div ui-ace kbn-ui-ace-keyboard-mode></div>`)($rootScope.$new());
|
||||
}));
|
||||
|
||||
it('should add the hint element', () => {
|
||||
expect(element.find('.uiAceKeyboardHint').length).to.be(1);
|
||||
});
|
||||
|
||||
describe('hint element', () => {
|
||||
it('should be tabable', () => {
|
||||
expect(element.find('.uiAceKeyboardHint').attr('tabindex')).to.be('0');
|
||||
});
|
||||
|
||||
it('should move focus to textbox and be inactive if pressed enter on it', () => {
|
||||
const textarea = element.find('textarea');
|
||||
sinon.spy(textarea[0], 'focus');
|
||||
const ev = angular.element.Event('keydown'); // eslint-disable-line new-cap
|
||||
ev.keyCode = ENTER_KEY;
|
||||
element.find('.uiAceKeyboardHint').trigger(ev);
|
||||
expect(textarea[0].focus.called).to.be(true);
|
||||
expect(element.find('.uiAceKeyboardHint').hasClass('uiAceKeyboardHint-isInactive')).to.be(true);
|
||||
});
|
||||
|
||||
it('should be shown again, when pressing Escape in ace editor', () => {
|
||||
const textarea = element.find('textarea');
|
||||
const hint = element.find('.uiAceKeyboardHint');
|
||||
sinon.spy(hint[0], 'focus');
|
||||
const ev = angular.element.Event('keydown'); // eslint-disable-line new-cap
|
||||
ev.keyCode = ESC_KEY_CODE;
|
||||
textarea.trigger(ev);
|
||||
expect(hint[0].focus.called).to.be(true);
|
||||
expect(hint.hasClass('uiAceKeyboardHint-isInactive')).to.be(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe('ui-ace textarea', () => {
|
||||
it('should not be tabable anymore', () => {
|
||||
expect(element.find('textarea').attr('tabindex')).to.be('-1');
|
||||
});
|
||||
});
|
||||
|
||||
});
|
80
src/ui/public/accessibility/kbn_ui_ace_keyboard_mode.js
Normal file
80
src/ui/public/accessibility/kbn_ui_ace_keyboard_mode.js
Normal file
|
@ -0,0 +1,80 @@
|
|||
/**
|
||||
* The `kbn-ui-ace-keyboard-mode` directive should be used on any element, that
|
||||
* `ui-ace` is used on. It will prevent the keyboard trap, that ui-ace usually
|
||||
* has, i.e. tabbing into the box won't give you any possibilities to leave
|
||||
* it via keyboard again, since tab inside the textbox works like a tab character.
|
||||
*
|
||||
* This directive won't change anything, if the user uses the mouse. But if she
|
||||
* tabs to the ace editor, an overlay will be shown, that you have to press Enter
|
||||
* to enter editing mode, and that it can be left by pressing Escape again.
|
||||
*
|
||||
* That way the ui-ace editor won't trap keyboard focus, and won't cause that
|
||||
* accessibility issue anymore.
|
||||
*/
|
||||
|
||||
import angular from 'angular';
|
||||
import { uiModules } from 'ui/modules';
|
||||
import './kbn_ui_ace_keyboard_mode.less';
|
||||
import { ESC_KEY_CODE, ENTER_KEY } from 'ui_framework/services';
|
||||
|
||||
let aceKeyboardModeId = 0;
|
||||
|
||||
uiModules.get('kibana').directive('kbnUiAceKeyboardMode', () => ({
|
||||
restrict: 'A',
|
||||
link(scope, element) {
|
||||
const uniqueId = `uiAceKeyboardHint-${scope.$id}-${aceKeyboardModeId++}`;
|
||||
|
||||
const hint = angular.element(
|
||||
`<div
|
||||
class="uiAceKeyboardHint"
|
||||
id="${uniqueId}"
|
||||
tabindex="0"
|
||||
role="application"
|
||||
>
|
||||
<p class="kuiText kuiVerticalRhythmSmall">
|
||||
Press Enter to start editing.
|
||||
</p>
|
||||
<p class="kuiText kuiVerticalRhythmSmall">
|
||||
When you’re done, press Escape to stop editing.
|
||||
</p>
|
||||
</div>
|
||||
`);
|
||||
|
||||
const uiAceTextbox = element.find('textarea');
|
||||
|
||||
function startEditing() {
|
||||
// We are not using ng-class in the element, so that we won't need to $compile it
|
||||
hint.addClass('uiAceKeyboardHint-isInactive');
|
||||
uiAceTextbox.focus();
|
||||
}
|
||||
|
||||
function enableOverlay() {
|
||||
hint.removeClass('uiAceKeyboardHint-isInactive');
|
||||
}
|
||||
|
||||
hint.keydown((ev) => {
|
||||
if (ev.keyCode === ENTER_KEY) {
|
||||
ev.preventDefault();
|
||||
startEditing();
|
||||
}
|
||||
});
|
||||
|
||||
uiAceTextbox.blur(() => {
|
||||
enableOverlay();
|
||||
});
|
||||
|
||||
uiAceTextbox.keydown((ev) => {
|
||||
if (ev.keyCode === ESC_KEY_CODE) {
|
||||
ev.preventDefault();
|
||||
ev.stopPropagation();
|
||||
enableOverlay();
|
||||
hint.focus();
|
||||
}
|
||||
});
|
||||
|
||||
hint.click(startEditing);
|
||||
// Prevent tabbing into the ACE textarea, we now handle all focusing for it
|
||||
uiAceTextbox.attr('tabindex', '-1');
|
||||
element.prepend(hint);
|
||||
}
|
||||
}));
|
26
src/ui/public/accessibility/kbn_ui_ace_keyboard_mode.less
Normal file
26
src/ui/public/accessibility/kbn_ui_ace_keyboard_mode.less
Normal file
|
@ -0,0 +1,26 @@
|
|||
@import (reference) "~ui/styles/variables";
|
||||
|
||||
.uiAceKeyboardHint {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
right: 0;
|
||||
left: 0;
|
||||
background: rgba(255, 255, 255, 0.7);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
text-align: center;
|
||||
opacity: 0;
|
||||
|
||||
&:focus {
|
||||
opacity: 1;
|
||||
border: 2px solid @globalColorBlue;
|
||||
z-index: 1000;
|
||||
}
|
||||
|
||||
&.uiAceKeyboardHint-isInactive {
|
||||
display: none;
|
||||
}
|
||||
}
|
|
@ -1,6 +1,7 @@
|
|||
<div
|
||||
json-input
|
||||
require-keys="true"
|
||||
kbn-ui-ace-keyboard-mode
|
||||
ui-ace="{
|
||||
mode: 'json',
|
||||
onLoad: aceLoaded
|
||||
|
|
|
@ -2,6 +2,7 @@ import 'ace';
|
|||
import _ from 'lodash';
|
||||
import { uiModules } from 'ui/modules';
|
||||
import template from './filter_query_dsl_editor.html';
|
||||
import 'ui/accessibility/kbn_ui_ace_keyboard_mode';
|
||||
|
||||
const module = uiModules.get('kibana');
|
||||
module.directive('filterQueryDslEditor', function () {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue