diff --git a/ui_framework/dist/ui_framework.css b/ui_framework/dist/ui_framework.css
index bd4e82c6b101d..a926f5382fb9b 100644
--- a/ui_framework/dist/ui_framework.css
+++ b/ui_framework/dist/ui_framework.css
@@ -688,6 +688,10 @@ input[type="button"] {
.kuiCodeEditorWrapper {
position: relative; }
+ .kuiCodeEditorWrapper .ace_hidden-cursors {
+ opacity: 0; }
+ .kuiCodeEditorWrapper.kuiCodeEditorWrapper-isEditing .ace_hidden-cursors {
+ opacity: 1; }
.kuiCodeEditorKeyboardHint {
position: absolute;
diff --git a/ui_framework/doc_site/src/views/code_editor/code_editor_example.js b/ui_framework/doc_site/src/views/code_editor/code_editor_example.js
index 35afd30f04473..e8af21a45ceb4 100644
--- a/ui_framework/doc_site/src/views/code_editor/code_editor_example.js
+++ b/ui_framework/doc_site/src/views/code_editor/code_editor_example.js
@@ -11,6 +11,9 @@ import {
import CodeEditor from './code_editor';
const codeEditorSource = require('!!raw-loader!./code_editor');
+import ReadOnly from './read_only';
+const readOnlySource = require('!!raw-loader!./read_only');
+
export default props => (
(
+
+
+
+
+
+
);
diff --git a/ui_framework/doc_site/src/views/code_editor/read_only.js b/ui_framework/doc_site/src/views/code_editor/read_only.js
new file mode 100644
index 0000000000000..5c455e5e89a60
--- /dev/null
+++ b/ui_framework/doc_site/src/views/code_editor/read_only.js
@@ -0,0 +1,27 @@
+import React, { Component } from 'react';
+
+import 'brace/mode/less';
+import 'brace/theme/github';
+
+import {
+ KuiCodeEditor
+} from '../../../../components';
+
+export default class extends Component {
+ state = {
+ value: '
This code is read only
'
+ };
+
+ render() {
+ return (
+
+ );
+ }
+}
diff --git a/ui_framework/src/components/code_editor/__snapshots__/code_editor.test.js.snap b/ui_framework/src/components/code_editor/__snapshots__/code_editor.test.js.snap
index c4faf3450cac8..971c88b219d5f 100644
--- a/ui_framework/src/components/code_editor/__snapshots__/code_editor.test.js.snap
+++ b/ui_framework/src/components/code_editor/__snapshots__/code_editor.test.js.snap
@@ -1,6 +1,6 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
-exports[`KuiCodeEditor hint element should be disabled when the ui ace box gains focus 1`] = `
+exports[`KuiCodeEditor behavior hint element should be disabled when the ui ace box gains focus 1`] = `
- Press Enter to start editing.
+ Press Enter to start
+ editing
+ .
- When you’re done, press Escape to stop editing.
+ When you’re done, press Escape to stop
+ editing
+ .
`;
-exports[`KuiCodeEditor hint element should be enabled when the ui ace box loses focus 1`] = `
+exports[`KuiCodeEditor behavior hint element should be enabled when the ui ace box loses focus 1`] = `
- Press Enter to start editing.
+ Press Enter to start
+ editing
+ .
- When you’re done, press Escape to stop editing.
+ When you’re done, press Escape to stop
+ editing
+ .
`;
@@ -60,12 +68,28 @@ exports[`KuiCodeEditor is rendered 1`] = `
- Press Enter to start editing.
+
+ Press Enter to start
+
+
+ editing
+
+
+ .
+
- When you’re done, press Escape to stop editing.
+
+ When you’re done, press Escape to stop
+
+
+ editing
+
+
+ .
+
`;
+
+exports[`KuiCodeEditor props isReadOnly renders alternate hint text 1`] = `
+
+
+
+
+ Press Enter to start
+
+
+ interacting with the code
+
+
+ .
+
+
+
+
+ When you’re done, press Escape to stop
+
+
+ interacting with the code
+
+
+ .
+
+
+
+
+
+
+
+
+
+
+
+
+ XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
+
+
+
+
+`;
diff --git a/ui_framework/src/components/code_editor/_code_editor.scss b/ui_framework/src/components/code_editor/_code_editor.scss
index bd246acc42223..880453440015a 100644
--- a/ui_framework/src/components/code_editor/_code_editor.scss
+++ b/ui_framework/src/components/code_editor/_code_editor.scss
@@ -1,5 +1,15 @@
.kuiCodeEditorWrapper {
position: relative;
+
+ .ace_hidden-cursors {
+ opacity: 0;
+ }
+
+ &.kuiCodeEditorWrapper-isEditing {
+ .ace_hidden-cursors {
+ opacity: 1;
+ }
+ }
}
.kuiCodeEditorKeyboardHint {
diff --git a/ui_framework/src/components/code_editor/code_editor.js b/ui_framework/src/components/code_editor/code_editor.js
index 5b8604693f0dd..b0ec265234c12 100644
--- a/ui_framework/src/components/code_editor/code_editor.js
+++ b/ui_framework/src/components/code_editor/code_editor.js
@@ -6,9 +6,9 @@ import AceEditor from 'react-ace';
import { htmlIdGenerator, keyCodes } from '../../../services';
export class KuiCodeEditor extends Component {
-
state = {
- isHintActive: true
+ isHintActive: true,
+ isEditing: false,
};
idGenerator = htmlIdGenerator();
@@ -30,6 +30,15 @@ export class KuiCodeEditor extends Component {
}
}
+ onFocusAce = (...args) => {
+ this.setState({
+ isEditing: true,
+ });
+ if (this.props.onFocus) {
+ this.props.onFocus(...args);
+ }
+ }
+
onBlurAce = (...args) => {
this.stopEditing();
if (this.props.onBlur) {
@@ -45,12 +54,17 @@ export class KuiCodeEditor extends Component {
};
startEditing = () => {
- this.setState({ isHintActive: false });
+ this.setState({
+ isHintActive: false,
+ });
this.aceEditor.editor.textInput.focus();
}
stopEditing() {
- this.setState({ isHintActive: true });
+ this.setState({
+ isHintActive: true,
+ isEditing: false,
+ });
}
render() {
@@ -58,42 +72,79 @@ export class KuiCodeEditor extends Component {
width,
height,
onBlur, // eslint-disable-line no-unused-vars
+ isReadOnly,
+ setOptions,
+ cursorStart,
...rest,
} = this.props;
- const classes = classNames('kuiCodeEditorKeyboardHint', {
+ const classes = classNames('kuiCodeEditorWrapper', {
+ 'kuiCodeEditorWrapper-isEditing': this.state.isEditing,
+ });
+
+ const promptClasses = classNames('kuiCodeEditorKeyboardHint', {
'kuiCodeEditorKeyboardHint-isInactive': !this.state.isHintActive
});
+ let filteredCursorStart;
+
+ const options = { ...setOptions };
+
+ if (isReadOnly) {
+ // Put the cursor at the beginning of the editor, so that it doesn't look like
+ // a prompt to begin typing.
+ filteredCursorStart = -1;
+
+ Object.assign(options, {
+ readOnly: true,
+ highlightActiveLine: false,
+ highlightGutterLine: false,
+ });
+ } else {
+ filteredCursorStart = cursorStart;
+ }
+
+ const activity =
+ isReadOnly
+ ? 'interacting with the code'
+ : 'editing';
+
+ const prompt = (
+ { this.editorHint = hint; }}
+ tabIndex="0"
+ role="button"
+ onClick={this.startEditing}
+ onKeyDown={this.onKeyDownHint}
+ data-test-subj="codeEditorHint"
+ >
+
+ Press Enter to start {activity}.
+
+
+
+ When you’re done, press Escape to stop {activity}.
+
+
+ );
+
return (
-
{ this.editorHint = hint; }}
- tabIndex="0"
- role="button"
- onClick={this.startEditing}
- onKeyDown={this.onKeyDownHint}
- data-test-subj="codeEditorHint"
- >
-
- Press Enter to start editing.
-
-
-
- When you’re done, press Escape to stop editing.
-
-
+ {prompt}
@@ -105,4 +156,11 @@ KuiCodeEditor.propTypes = {
width: PropTypes.string,
height: PropTypes.string,
onBlur: PropTypes.func,
+ isReadOnly: PropTypes.bool,
+ setOptions: PropTypes.object,
+ cursorStart: PropTypes.number,
+};
+
+KuiCodeEditor.defaultProps = {
+ setOptions: {},
};
diff --git a/ui_framework/src/components/code_editor/code_editor.test.js b/ui_framework/src/components/code_editor/code_editor.test.js
index de9bd430d4b39..e204f4bf02edb 100644
--- a/ui_framework/src/components/code_editor/code_editor.test.js
+++ b/ui_framework/src/components/code_editor/code_editor.test.js
@@ -14,53 +14,64 @@ jest.mock('../../services/accessibility/html_id_generator', () => ({
}));
describe('KuiCodeEditor', () => {
- let element;
-
- beforeEach(() => {
- element = mount();
- });
-
test('is rendered', () => {
- const component = mount();
+ const component = mount();
expect(takeMountedSnapshot(component)).toMatchSnapshot();
});
- describe('hint element', () => {
- test('should be tabable', () => {
- expect(element.find('[data-test-subj="codeEditorHint"]').prop('tabIndex')).toBe('0');
+ describe('props', () => {
+ describe('isReadOnly', () => {
+ test(`renders alternate hint text`, () => {
+ const component = mount();
+ expect(takeMountedSnapshot(component)).toMatchSnapshot();
+ });
});
+ });
- test('should be disabled when the ui ace box gains focus', () => {
- const hint = element.find('[data-test-subj="codeEditorHint"]');
- hint.simulate('keydown', { keyCode: keyCodes.ENTER });
- expect(hint).toMatchSnapshot();
- });
+ describe('behavior', () => {
+ let component;
- test('should be enabled when the ui ace box loses focus', () => {
- const hint = element.find('[data-test-subj="codeEditorHint"]');
- hint.simulate('keydown', { keyCode: keyCodes.ENTER });
- element.instance().onBlurAce();
- expect(hint).toMatchSnapshot();
+ beforeEach(() => {
+ component = mount();
});
- });
- describe('interaction', () => {
- test('bluring the ace textbox should call a passed onBlur prop', () => {
- const blurSpy = sinon.spy();
- const el = mount();
- el.instance().onBlurAce();
- expect(blurSpy.called).toBe(true);
+ describe('hint element', () => {
+ test('should be tabable', () => {
+ expect(component.find('[data-test-subj="codeEditorHint"]').prop('tabIndex')).toBe('0');
+ });
+
+ test('should be disabled when the ui ace box gains focus', () => {
+ const hint = component.find('[data-test-subj="codeEditorHint"]');
+ hint.simulate('keydown', { keyCode: keyCodes.ENTER });
+ expect(hint).toMatchSnapshot();
+ });
+
+ test('should be enabled when the ui ace box loses focus', () => {
+ const hint = component.find('[data-test-subj="codeEditorHint"]');
+ hint.simulate('keydown', { keyCode: keyCodes.ENTER });
+ component.instance().onBlurAce();
+ expect(hint).toMatchSnapshot();
+ });
});
- test('pressing escape in ace textbox will enable overlay', () => {
- element.instance().onKeydownAce({
- preventDefault: () => {},
- stopPropagation: () => {},
- keyCode: keyCodes.ESCAPE
+ describe('interaction', () => {
+ test('bluring the ace textbox should call a passed onBlur prop', () => {
+ const blurSpy = sinon.spy();
+ const el = mount();
+ el.instance().onBlurAce();
+ expect(blurSpy.called).toBe(true);
+ });
+
+ test('pressing escape in ace textbox will enable overlay', () => {
+ component.instance().onKeydownAce({
+ preventDefault: () => {},
+ stopPropagation: () => {},
+ keyCode: keyCodes.ESCAPE
+ });
+ expect(
+ component.find('[data-test-subj="codeEditorHint"]').matchesElement(document.activeElement)
+ ).toBe(true);
});
- expect(
- element.find('[data-test-subj="codeEditorHint"]').matchesElement(document.activeElement)
- ).toBe(true);
});
});
});