[7.x] Delete src/legacy/ui/public folder (#76085) (#76351)

* Delete src/legacy/ui/public folder (#76085)

* delete src/legacy/ui/public folder

* remove jest.mock('ui/XXX'); from tests

* adapt stubbedLogstashIndexPatternService

* remove delete keys from translation files

* fix types import with Capabilities

* remove legacy test utils

* remove dead file referencing ui/newPlatform

* move saved-object-finder styles to timelion plugin
# Conflicts:
#	src/legacy/ui/public/new_platform/new_platform.test.mocks.ts
#	src/legacy/ui/public/new_platform/new_platform.ts
#	src/test_utils/public/no_digest_promises.js
#	x-pack/plugins/snapshot_restore/__jest__/client_integration/home.test.ts
#	x-pack/plugins/snapshot_restore/__jest__/client_integration/policy_edit.test.ts
#	x-pack/plugins/snapshot_restore/__jest__/client_integration/repository_add.test.ts
#	x-pack/plugins/snapshot_restore/__jest__/client_integration/repository_edit.test.ts

* fix 7.x code

* restore legacy docLinks file
This commit is contained in:
Pierre Gayvallet 2020-09-01 15:53:31 +02:00 committed by GitHub
parent 6794779296
commit b20030187c
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
296 changed files with 91 additions and 25523 deletions

View file

@ -28,12 +28,6 @@ const createMockStorage = () => ({
length: 0,
});
jest.mock('ui/chrome', () => {
return {
getBasePath: () => `/some/base/path`,
};
});
const historyName = 'testHistory';
const historyLimit = 10;
const payload = [

View file

@ -56,7 +56,6 @@ export default {
'@elastic/eui/lib/(.*)?': '<rootDir>/node_modules/@elastic/eui/test-env/$1',
'^src/plugins/(.*)': '<rootDir>/src/plugins/$1',
'^plugins/([^/.]*)(.*)': '<rootDir>/src/legacy/core_plugins/$1/public$2',
'^ui/(.*)': '<rootDir>/src/legacy/ui/public/$1',
'^uiExports/(.*)': '<rootDir>/src/dev/jest/mocks/file_mock.js',
'^test_utils/(.*)': '<rootDir>/src/test_utils/public/$1',
'^fixtures/(.*)': '<rootDir>/src/fixtures/$1',

View file

@ -34,10 +34,6 @@
* The mocks that are enabled that way live inside the `__mocks__` folders beside their implementation files.
*/
jest.mock('ui/metadata');
jest.mock('ui/documentation_links/documentation_links');
jest.mock('ui/chrome');
jest.mock('moment-timezone', () => {
// We always want to mock the timezone moment-timezone guesses, since otherwise
// test results might be depending on which time zone you are running them.

View file

@ -21,7 +21,12 @@ import StubIndexPattern from 'test_utils/stub_index_pattern';
import stubbedLogstashFields from 'fixtures/logstash_fields';
import { getKbnFieldType } from '../plugins/data/common';
import { npSetup } from '../legacy/ui/public/new_platform/new_platform.karma_mock';
import { uiSettingsServiceMock } from '../core/public/ui_settings/ui_settings_service.mock';
const uiSettingSetupMock = uiSettingsServiceMock.createSetupContract();
uiSettingSetupMock.get.mockImplementation((item, defaultValue) => {
return defaultValue;
});
export default function stubbedLogstashIndexPatternService() {
const mockLogstashFields = stubbedLogstashFields();
@ -41,13 +46,9 @@ export default function stubbedLogstashIndexPatternService() {
};
});
const indexPattern = new StubIndexPattern(
'logstash-*',
(cfg) => cfg,
'time',
fields,
npSetup.core
);
const indexPattern = new StubIndexPattern('logstash-*', (cfg) => cfg, 'time', fields, {
uiSettings: uiSettingSetupMock,
});
indexPattern.id = 'logstash-*';
indexPattern.isTimeNanosBased = () => false;

View file

@ -1,3 +0,0 @@
rules:
no-console: 2
'import/no-default-export': error

View file

@ -1,25 +0,0 @@
/*
* Licensed to Elasticsearch B.V. under one or more contributor
* license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright
* ownership. Elasticsearch B.V. licenses this file to you under
* the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
import { metadata as metadataImpl } from '../metadata';
export const metadata: typeof metadataImpl = {
branch: 'jest-metadata-mock-branch',
version: '42.23.26',
};

View file

@ -1,244 +0,0 @@
/*
* Licensed to Elasticsearch B.V. under one or more contributor
* license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright
* ownership. Elasticsearch B.V. licenses this file to you under
* the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
import _ from 'lodash';
import sinon from 'sinon';
import ngMock from 'ng_mock';
import { EventsProvider } from '../events';
import expect from '@kbn/expect';
import '../private';
import { createDefer } from 'ui/promises';
import { createLegacyClass } from '../utils/legacy_class';
describe('Events', function () {
require('test_utils/no_digest_promises').activateForSuite();
let Events;
let Promise;
let eventsInstance;
beforeEach(ngMock.module('kibana'));
beforeEach(
ngMock.inject(function ($injector, Private) {
Promise = $injector.get('Promise');
Events = Private(EventsProvider);
eventsInstance = new Events();
})
);
it('should handle on events', function () {
const obj = new Events();
const prom = obj.on('test', function (message) {
expect(message).to.equal('Hello World');
});
obj.emit('test', 'Hello World');
return prom;
});
it('should work with inherited objects', function () {
createLegacyClass(MyEventedObject).inherits(Events);
function MyEventedObject() {
MyEventedObject.Super.call(this);
}
const obj = new MyEventedObject();
const prom = obj.on('test', function (message) {
expect(message).to.equal('Hello World');
});
obj.emit('test', 'Hello World');
return prom;
});
it('should clear events when off is called', function () {
const obj = new Events();
obj.on('test', _.noop);
expect(obj._listeners).to.have.property('test');
expect(obj._listeners.test).to.have.length(1);
obj.off();
expect(obj._listeners).to.not.have.property('test');
});
it('should clear a specific handler when off is called for an event', function () {
const obj = new Events();
const handler1 = sinon.stub();
const handler2 = sinon.stub();
obj.on('test', handler1);
obj.on('test', handler2);
expect(obj._listeners).to.have.property('test');
obj.off('test', handler1);
return obj.emit('test', 'Hello World').then(function () {
sinon.assert.calledOnce(handler2);
sinon.assert.notCalled(handler1);
});
});
it('should clear a all handlers when off is called for an event', function () {
const obj = new Events();
const handler1 = sinon.stub();
obj.on('test', handler1);
expect(obj._listeners).to.have.property('test');
obj.off('test');
expect(obj._listeners).to.not.have.property('test');
return obj.emit('test', 'Hello World').then(function () {
sinon.assert.notCalled(handler1);
});
});
it('should handle multiple identical emits in the same tick', function () {
const obj = new Events();
const handler1 = sinon.stub();
obj.on('test', handler1);
const emits = [obj.emit('test', 'one'), obj.emit('test', 'two'), obj.emit('test', 'three')];
return Promise.all(emits).then(function () {
expect(handler1.callCount).to.be(emits.length);
expect(handler1.getCall(0).calledWith('one')).to.be(true);
expect(handler1.getCall(1).calledWith('two')).to.be(true);
expect(handler1.getCall(2).calledWith('three')).to.be(true);
});
});
it('should handle emits from the handler', function () {
const obj = new Events();
const secondEmit = createDefer(Promise);
const handler1 = sinon.spy(function () {
if (handler1.calledTwice) {
return;
}
obj.emit('test').then(_.bindKey(secondEmit, 'resolve'));
});
obj.on('test', handler1);
return Promise.all([obj.emit('test'), secondEmit.promise]).then(function () {
expect(handler1.callCount).to.be(2);
});
});
it('should only emit to handlers registered before emit is called', function () {
const obj = new Events();
const handler1 = sinon.stub();
const handler2 = sinon.stub();
obj.on('test', handler1);
const emits = [obj.emit('test', 'one'), obj.emit('test', 'two'), obj.emit('test', 'three')];
return Promise.all(emits).then(function () {
expect(handler1.callCount).to.be(emits.length);
obj.on('test', handler2);
const emits2 = [obj.emit('test', 'four'), obj.emit('test', 'five'), obj.emit('test', 'six')];
return Promise.all(emits2).then(function () {
expect(handler1.callCount).to.be(emits.length + emits2.length);
expect(handler2.callCount).to.be(emits2.length);
});
});
});
it('should pass multiple arguments from the emitter', function () {
const obj = new Events();
const handler = sinon.stub();
const payload = ['one', { hello: 'tests' }, null];
obj.on('test', handler);
return obj.emit('test', payload[0], payload[1], payload[2]).then(function () {
expect(handler.callCount).to.be(1);
expect(handler.calledWithExactly(payload[0], payload[1], payload[2])).to.be(true);
});
});
it('should preserve the scope of the handler', function () {
const obj = new Events();
const expected = 'some value';
let testValue;
function handler() {
testValue = this.getVal();
}
handler.getVal = _.constant(expected);
obj.on('test', handler);
return obj.emit('test').then(function () {
expect(testValue).to.equal(expected);
});
});
it('should always emit in the same order', function () {
const handler = sinon.stub();
const obj = new Events();
obj.on('block', _.partial(handler, 'block'));
obj.on('last', _.partial(handler, 'last'));
return Promise.all([
obj.emit('block'),
obj.emit('block'),
obj.emit('block'),
obj.emit('block'),
obj.emit('block'),
obj.emit('block'),
obj.emit('block'),
obj.emit('block'),
obj.emit('block'),
obj.emit('last'),
]).then(function () {
expect(handler.callCount).to.be(10);
handler.args.forEach(function (args, i) {
expect(args[0]).to.be(i < 9 ? 'block' : 'last');
});
});
});
it('calls emitted handlers asynchronously', (done) => {
const listenerStub = sinon.stub();
eventsInstance.on('test', listenerStub);
eventsInstance.emit('test');
sinon.assert.notCalled(listenerStub);
setTimeout(() => {
sinon.assert.calledOnce(listenerStub);
done();
}, 100);
});
it('calling off after an emit that has not yet triggered the handler, will not call the handler', (done) => {
const listenerStub = sinon.stub();
eventsInstance.on('test', listenerStub);
eventsInstance.emit('test');
// It's called asynchronously so it shouldn't be called yet.
sinon.assert.notCalled(listenerStub);
eventsInstance.off('test', listenerStub);
setTimeout(() => {
sinon.assert.notCalled(listenerStub);
done();
}, 100);
});
});

View file

@ -1,29 +0,0 @@
/*
* Licensed to Elasticsearch B.V. under one or more contributor
* license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright
* ownership. Elasticsearch B.V. licenses this file to you under
* the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
import expect from '@kbn/expect';
import { metadata } from '../metadata';
describe('ui/metadata', () => {
it('is immutable', () => {
expect(() => (metadata.foo = 'something')).to.throw;
expect(() => (metadata.version = 'something')).to.throw;
expect(() => (metadata.vars = {})).to.throw;
expect(() => (metadata.vars.kbnIndex = 'something')).to.throw;
});
});

View file

@ -1,9 +0,0 @@
// Prefix all styles with "kbn" to avoid conflicts.
// Examples
// kbnChart
// kbnChart__legend
// kbnChart__legend--small
// kbnChart__legend-isLoading
@import './accessibility/index';
@import './directives/index';

View file

@ -1,127 +0,0 @@
/*
* Licensed to Elasticsearch B.V. under one or more contributor
* license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright
* ownership. Elasticsearch B.V. licenses this file to you under
* the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
import angular from 'angular';
import sinon from 'sinon';
import expect from '@kbn/expect';
import ngMock from 'ng_mock';
import '../kbn_accessible_click';
import { keys } from '@elastic/eui';
describe('kbnAccessibleClick directive', () => {
let $compile;
let $rootScope;
beforeEach(ngMock.module('kibana'));
beforeEach(
ngMock.inject(function (_$compile_, _$rootScope_) {
$compile = _$compile_;
$rootScope = _$rootScope_;
})
);
describe('throws an error', () => {
it('when the element is a button', () => {
const html = `<button kbn-accessible-click></button>`;
expect(() => {
$compile(html)($rootScope);
}).to.throwError(/kbnAccessibleClick doesn't need to be used on a button./);
});
it('when the element is a link with an href', () => {
const html = `<a href="#" kbn-accessible-click></a>`;
expect(() => {
$compile(html)($rootScope);
}).to.throwError(
/kbnAccessibleClick doesn't need to be used on a link if it has a href attribute./
);
});
it(`when the element doesn't have an ng-click`, () => {
const html = `<div kbn-accessible-click></div>`;
expect(() => {
$compile(html)($rootScope);
}).to.throwError(/kbnAccessibleClick requires ng-click to be defined on its element./);
});
});
describe(`doesn't throw an error`, () => {
it('when the element is a link without an href', () => {
const html = `<a ng-click="noop" kbn-accessible-click></a>`;
expect(() => {
$compile(html)($rootScope);
}).not.to.throwError();
});
});
describe('adds accessibility attributes', () => {
it('tabindex', () => {
const html = `<div ng-click="noop" kbn-accessible-click></div>`;
const element = $compile(html)($rootScope);
expect(element.attr('tabindex')).to.be('0');
});
it('role', () => {
const html = `<div ng-click="noop" kbn-accessible-click></div>`;
const element = $compile(html)($rootScope);
expect(element.attr('role')).to.be('button');
});
});
describe(`doesn't override pre-existing accessibility attributes`, () => {
it('tabindex', () => {
const html = `<div ng-click="noop" kbn-accessible-click tabindex="1"></div>`;
const element = $compile(html)($rootScope);
expect(element.attr('tabindex')).to.be('1');
});
it('role', () => {
const html = `<div ng-click="noop" kbn-accessible-click role="submit"></div>`;
const element = $compile(html)($rootScope);
expect(element.attr('role')).to.be('submit');
});
});
describe(`calls ng-click`, () => {
let scope;
let element;
beforeEach(function () {
scope = $rootScope.$new();
scope.handleClick = sinon.stub();
const html = `<div ng-click="handleClick()" kbn-accessible-click></div>`;
element = $compile(html)(scope);
});
it(`on ENTER keyup`, () => {
const e = angular.element.Event('keyup'); // eslint-disable-line new-cap
e.key = keys.ENTER;
element.trigger(e);
sinon.assert.calledOnce(scope.handleClick);
});
it(`on SPACE keyup`, () => {
const e = angular.element.Event('keyup'); // eslint-disable-line new-cap
e.key = keys.SPACE;
element.trigger(e);
sinon.assert.calledOnce(scope.handleClick);
});
});
});

View file

@ -1,129 +0,0 @@
/*
* Licensed to Elasticsearch B.V. under one or more contributor
* license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright
* ownership. Elasticsearch B.V. licenses this file to you under
* the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
import angular from 'angular';
import sinon from 'sinon';
import expect from '@kbn/expect';
import ngMock from 'ng_mock';
import '../kbn_ui_ace_keyboard_mode';
import { keys } from '@elastic/eui';
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('.kbnUiAceKeyboardHint').length).to.be(1);
});
describe('hint element', () => {
it('should be tabable', () => {
expect(element.find('.kbnUiAceKeyboardHint').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.key = keys.ENTER;
element.find('.kbnUiAceKeyboardHint').trigger(ev);
expect(textarea[0].focus.called).to.be(true);
expect(
element.find('.kbnUiAceKeyboardHint').hasClass('kbnUiAceKeyboardHint-isInactive')
).to.be(true);
});
it('should be shown again, when pressing Escape in ace editor', () => {
const textarea = element.find('textarea');
const hint = element.find('.kbnUiAceKeyboardHint');
sinon.spy(hint[0], 'focus');
const ev = angular.element.Event('keydown'); // eslint-disable-line new-cap
ev.key = keys.ESCAPE;
textarea.trigger(ev);
expect(hint[0].focus.called).to.be(true);
expect(hint.hasClass('kbnUiAceKeyboardHint-isInactive')).to.be(false);
});
});
describe('ui-ace textarea', () => {
it('should not be tabable anymore', () => {
expect(element.find('textarea').attr('tabindex')).to.be('-1');
});
});
});
describe('kbnUiAceKeyboardModeService', () => {
let element;
beforeEach(ngMock.module('kibana'));
beforeEach(
ngMock.inject(($compile, $rootScope, kbnUiAceKeyboardModeService) => {
const scope = $rootScope.$new();
element = $compile(`<div ui-ace></div>`)(scope);
kbnUiAceKeyboardModeService.initialize(scope, element);
})
);
it('should add the hint element', () => {
expect(element.find('.kbnUiAceKeyboardHint').length).to.be(1);
});
describe('hint element', () => {
it('should be tabable', () => {
expect(element.find('.kbnUiAceKeyboardHint').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.key = keys.ENTER;
element.find('.kbnUiAceKeyboardHint').trigger(ev);
expect(textarea[0].focus.called).to.be(true);
expect(
element.find('.kbnUiAceKeyboardHint').hasClass('kbnUiAceKeyboardHint-isInactive')
).to.be(true);
});
it('should be shown again, when pressing Escape in ace editor', () => {
const textarea = element.find('textarea');
const hint = element.find('.kbnUiAceKeyboardHint');
sinon.spy(hint[0], 'focus');
const ev = angular.element.Event('keydown'); // eslint-disable-line new-cap
ev.key = keys.ESCAPE;
textarea.trigger(ev);
expect(hint[0].focus.called).to.be(true);
expect(hint.hasClass('kbnUiAceKeyboardHint-isInactive')).to.be(false);
});
});
describe('ui-ace textarea', () => {
it('should not be tabable anymore', () => {
expect(element.find('textarea').attr('tabindex')).to.be('-1');
});
});
});

View file

@ -1,56 +0,0 @@
/*
* Licensed to Elasticsearch B.V. under one or more contributor
* license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright
* ownership. Elasticsearch B.V. licenses this file to you under
* the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
import sinon from 'sinon';
import expect from '@kbn/expect';
import ngMock from 'ng_mock';
import '../scrollto_activedescendant';
describe('scrolltoActivedescendant directive', () => {
let $compile;
let $rootScope;
beforeEach(ngMock.module('kibana'));
beforeEach(
ngMock.inject((_$compile_, _$rootScope_) => {
$compile = _$compile_;
$rootScope = _$rootScope_;
})
);
it('should call scrollIntoView on aria-activedescendant changes', () => {
const scope = $rootScope.$new();
scope.ad = '';
const element = $compile(`<div aria-activedescendant="{{ad}}" scrollto-activedescendant>
<span id="child1"></span>
<span id="child2"></span>
</div>`)(scope);
const child1 = element.find('#child1');
const child2 = element.find('#child2');
sinon.spy(child1[0], 'scrollIntoView');
sinon.spy(child2[0], 'scrollIntoView');
scope.ad = 'child1';
scope.$digest();
expect(child1[0].scrollIntoView.calledOnce).to.be.eql(true);
scope.ad = 'child2';
scope.$digest();
expect(child2[0].scrollIntoView.calledOnce).to.be.eql(true);
});
});

View file

@ -1 +0,0 @@
@import './kbn_ui_ace_keyboard_mode';

View file

@ -1,24 +0,0 @@
.kbnUiAceKeyboardHint {
position: absolute;
top: 0;
bottom: 0;
right: 0;
left: 0;
background: transparentize($euiColorEmptyShade, 0.3);
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
text-align: center;
opacity: 0;
&:focus {
opacity: 1;
border: 2px solid $euiColorPrimary;
z-index: 1000;
}
&.kbnUiAceKeyboardHint-isInactive {
display: none;
}
}

View file

@ -1,39 +0,0 @@
/*
* Licensed to Elasticsearch B.V. under one or more contributor
* license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright
* ownership. Elasticsearch B.V. licenses this file to you under
* the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
import 'angular-aria';
import { uiModules } from '../modules';
/**
* This module will take care of attaching appropriate aria tags related to some angular stuff,
* e.g. it will attach aria-invalid if the model state is set to invalid.
*
* You can find more infos in the official documentation: https://docs.angularjs.org/api/ngAria.
*
* Three settings are disabled: it won't automatically attach `tabindex`, `role=button` or
* handling keyboard events for `ngClick` directives. Kibana uses `kbnAccessibleClick` to handle
* those cases where you need an `ngClick` non button element to have keyboard access.
*/
uiModules.get('kibana', ['ngAria']).config(($ariaProvider) => {
$ariaProvider.config({
bindKeydown: false,
bindRoleForClick: false,
tabindex: false,
});
});

View file

@ -1,22 +0,0 @@
/*
* Licensed to Elasticsearch B.V. under one or more contributor
* license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright
* ownership. Elasticsearch B.V. licenses this file to you under
* the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
import './angular_aria';
import './kbn_accessible_click';
import './scrollto_activedescendant';

View file

@ -1,43 +0,0 @@
/*
* Licensed to Elasticsearch B.V. under one or more contributor
* license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright
* ownership. Elasticsearch B.V. licenses this file to you under
* the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
/**
* Interactive elements must be able to receive focus.
*
* Ideally, this means using elements that are natively keyboard accessible (<a href="">,
* <input type="button">, or <button>). Note that links should be used when navigating and buttons
* should be used when performing an action on the page.
*
* If you need to use a <div>, <p>, or <a> without the href attribute, then you need to allow
* them to receive focus and to respond to keyboard input. The workaround is to:
*
* - Give the element tabindex="0" so that it can receive keyboard focus.
* - Add a JavaScript onkeyup event handler that triggers element functionality if the Enter key
* is pressed while the element is focused. This is necessary because some browsers do not trigger
* onclick events for such elements when activated via the keyboard.
* - If the item is meant to function as a button, the onkeyup event handler should also detect the
* Spacebar in addition to the Enter key, and the element should be given role="button".
*
* Apply this directive to any of these elements to automatically do the above.
*/
import { uiModules } from '../modules';
import { KbnAccessibleClickProvider } from '../../../../plugins/kibana_legacy/public';
uiModules.get('kibana').directive('kbnAccessibleClick', KbnAccessibleClickProvider);

View file

@ -1,129 +0,0 @@
/*
* Licensed to Elasticsearch B.V. under one or more contributor
* license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright
* ownership. Elasticsearch B.V. licenses this file to you under
* the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
/**
* 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 '../modules';
import { keys } from '@elastic/eui';
let aceKeyboardModeId = 0;
uiModules
.get('kibana')
.factory('kbnUiAceKeyboardModeService', () => ({
initialize(scope, element) {
const uniqueId = `kbnUiAceKeyboardHint-${scope.$id}-${aceKeyboardModeId++}`;
const hint = angular.element(
`<div
class="kbnUiAceKeyboardHint"
id="${uniqueId}"
tabindex="0"
role="application"
>
<p class="kuiText kuiVerticalRhythmSmall">
Press Enter to start editing.
</p>
<p class="kuiText kuiVerticalRhythmSmall">
When you&rsquo;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('kbnUiAceKeyboardHint-isInactive');
uiAceTextbox.focus();
}
function enableOverlay() {
hint.removeClass('kbnUiAceKeyboardHint-isInactive');
}
hint.keydown((ev) => {
if (ev.key === keys.ENTER) {
ev.preventDefault();
startEditing();
}
});
uiAceTextbox.blur(() => {
enableOverlay();
});
let isAutoCompleterOpen;
// We have to capture this event on the 'capture' phase, otherwise Ace will have already
// dismissed the autocompleter when the user hits ESC.
document.addEventListener(
'keydown',
() => {
const autoCompleter = document.querySelector('.ace_autocomplete');
if (!autoCompleter) {
isAutoCompleterOpen = false;
return;
}
// The autoComplete is just hidden when it's closed, not removed from the DOM.
isAutoCompleterOpen = autoCompleter.style.display !== 'none';
},
{ capture: true }
);
uiAceTextbox.keydown((ev) => {
if (ev.key === keys.ESCAPE) {
// If the autocompletion context menu is open then we want to let ESC close it but
// **not** exit out of editing mode.
if (!isAutoCompleterOpen) {
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);
},
}))
.directive('kbnUiAceKeyboardMode', (kbnUiAceKeyboardModeService) => ({
restrict: 'A',
link(scope, element) {
kbnUiAceKeyboardModeService.initialize(scope, element);
},
}));

View file

@ -1,43 +0,0 @@
/*
* Licensed to Elasticsearch B.V. under one or more contributor
* license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright
* ownership. Elasticsearch B.V. licenses this file to you under
* the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
/**
* This directive can be applied to an element, that has also aria-activedescendant applied.
* It will make sure, that whenever aria-activedescendant changes, the new element
* referenced by it, will be scrolled into visible view, by calling its `scrollIntoView`
* method.
*/
import { uiModules } from '../modules';
uiModules.get('kibana').directive('scrolltoActivedescendant', () => ({
link(scope, element, attrs) {
scope.$watch(
() => attrs.ariaActivedescendant,
(val) => {
if (val) {
const activeDescendant = element.find(`#${val}`);
if (activeDescendant.length) {
activeDescendant[0].scrollIntoView();
}
}
}
);
},
}));

View file

@ -1,28 +0,0 @@
/*
* Licensed to Elasticsearch B.V. under one or more contributor
* license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright
* ownership. Elasticsearch B.V. licenses this file to you under
* the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
import 'jquery';
import 'angular';
// required for `ngSanitize` angular module
import 'angular-sanitize';
import 'ui-select/dist/select';
import { uiModules } from 'ui/modules';
uiModules.get('kibana', ['ui.select', 'ngSanitize']);

View file

@ -1,20 +0,0 @@
/*
* Licensed to Elasticsearch B.V. under one or more contributor
* license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright
* ownership. Elasticsearch B.V. licenses this file to you under
* the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
import '../accessibility';

View file

@ -1,22 +0,0 @@
/*
* Licensed to Elasticsearch B.V. under one or more contributor
* license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright
* ownership. Elasticsearch B.V. licenses this file to you under
* the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
import './accessibility';
import './modules';
import './settings';

View file

@ -1,35 +0,0 @@
/*
* Licensed to Elasticsearch B.V. under one or more contributor
* license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright
* ownership. Elasticsearch B.V. licenses this file to you under
* the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
import 'angular';
import '../chrome';
import '../config';
import '../notify';
import '../private';
import '../promises';
import '../state_management/app_state';
import '../state_management/global_state';
import '../url';
import '../directives/watch_multi';
import '../react_components';
import '../i18n';
import '@elastic/ui-ace';
import { uiModules } from 'ui/modules';
uiModules.get('kibana', ['ui.ace']);

View file

@ -1,20 +0,0 @@
/*
* Licensed to Elasticsearch B.V. under one or more contributor
* license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright
* ownership. Elasticsearch B.V. licenses this file to you under
* the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
/** Left intentionally empty to avoid breaking plugins that import this file during the NP migration */

View file

@ -1,88 +0,0 @@
/*
* Licensed to Elasticsearch B.V. under one or more contributor
* license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright
* ownership. Elasticsearch B.V. licenses this file to you under
* the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
import sinon from 'sinon';
import expect from '@kbn/expect';
import ngMock from 'ng_mock';
import { Binder } from '..';
import $ from 'jquery';
describe('Binder class', function () {
let $scope;
beforeEach(ngMock.module('kibana'));
beforeEach(
ngMock.inject(function ($rootScope) {
$scope = $rootScope.$new();
})
);
describe('Constructing with a $scope', function () {
it('accepts a $scope and listens for $destroy', function () {
sinon.stub($scope, '$on');
new Binder($scope);
expect($scope.$on.callCount).to.be(1);
expect($scope.$on.args[0][0]).to.be('$destroy');
});
it('unbinds when the $scope is destroyed', function () {
const binder = new Binder($scope);
sinon.stub(binder, 'destroy');
$scope.$destroy();
expect(binder.destroy.callCount).to.be(1);
});
});
describe('Binder#on', function () {
it('binds to normal event emitters', function () {
const binder = new Binder();
const emitter = {
on: sinon.stub(),
removeListener: sinon.stub(),
};
const handler = sinon.stub();
binder.on(emitter, 'click', handler);
expect(emitter.on.callCount).to.be(1);
expect(emitter.on.args[0][0]).to.be('click');
expect(emitter.on.args[0][1]).to.be(handler);
binder.destroy();
expect(emitter.removeListener.callCount).to.be(1);
expect(emitter.removeListener.args[0][0]).to.be('click');
expect(emitter.removeListener.args[0][1]).to.be(handler);
});
});
describe('Binder#jqOn', function () {
it('binds jquery event handlers', function () {
const binder = new Binder();
const el = document.createElement('div');
const handler = sinon.stub();
binder.jqOn(el, 'click', handler);
$(el).click();
expect(handler.callCount).to.be(1);
binder.destroy();
$(el).click();
expect(handler.callCount).to.be(1);
});
});
});

View file

@ -1,53 +0,0 @@
/*
* Licensed to Elasticsearch B.V. under one or more contributor
* license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright
* ownership. Elasticsearch B.V. licenses this file to you under
* the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
import d3 from 'd3';
import $ from 'jquery';
import { BinderBase } from '../../../utils/binder';
export class Binder extends BinderBase {
constructor($scope) {
super();
// support auto-binding to $scope objects
if ($scope) {
$scope.$on('$destroy', () => this.destroy());
}
}
jqOn(el, ...args) {
const $el = $(el);
$el.on(...args);
this.disposal.push(() => $el.off(...args));
}
fakeD3Bind(el, event, handler) {
this.jqOn(el, event, (e) => {
// mimic https://github.com/mbostock/d3/blob/3abb00113662463e5c19eb87cd33f6d0ddc23bc0/src/selection/on.js#L87-L94
const o = d3.event; // Events can be reentrant (e.g., focus).
d3.event = e;
try {
handler.apply(this, [this.__data__]);
} finally {
d3.event = o;
}
});
}
}

View file

@ -1,20 +0,0 @@
/*
* Licensed to Elasticsearch B.V. under one or more contributor
* license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright
* ownership. Elasticsearch B.V. licenses this file to you under
* the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
export { Binder } from './binder';

View file

@ -1,55 +0,0 @@
/*
* Licensed to Elasticsearch B.V. under one or more contributor
* license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright
* ownership. Elasticsearch B.V. licenses this file to you under
* the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
import _ from 'lodash';
export function BoundToConfigObjProvider(config) {
/**
* Create an object with properties that may be bound to config values.
* The input object is basically cloned unless one of it's own properties
* resolved to a string value that starts with an equal sign. When that is
* found, that property is forever bound to the corresponding config key.
*
* example:
*
* // name is cloned, height is bound to the defaultHeight config key
* { name: 'john', height: '=defaultHeight' };
*
* @param {Object} input
* @return {Object}
*/
function BoundToConfigObj(input) {
const self = this;
_.forOwn(input, function (value, prop) {
if (!_.isString(value) || value.charAt(0) !== '=') {
self[prop] = value;
return;
}
const configKey = value.substr(1);
config.watch(configKey, function update(value) {
self[prop] = value;
});
});
}
return BoundToConfigObj;
}

View file

@ -1,29 +0,0 @@
/*
* Licensed to Elasticsearch B.V. under one or more contributor
* license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright
* ownership. Elasticsearch B.V. licenses this file to you under
* the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
import { npStart } from 'ui/new_platform';
import { Capabilities as UICapabilities } from '../../../../core/public';
export { UICapabilities };
export const capabilities = {
get() {
return npStart.core.application.capabilities;
},
};

View file

@ -1,21 +0,0 @@
/*
* Licensed to Elasticsearch B.V. under one or more contributor
* license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright
* ownership. Elasticsearch B.V. licenses this file to you under
* the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
export { UICapabilitiesProvider } from './ui_capabilities_provider';
export { injectUICapabilities } from './inject_ui_capabilities';

View file

@ -1,116 +0,0 @@
/*
* Licensed to Elasticsearch B.V. under one or more contributor
* license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright
* ownership. Elasticsearch B.V. licenses this file to you under
* the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
jest.mock('ui/capabilities', () => ({
capabilities: {
get: () => ({
uiCapability1: true,
uiCapability2: {
nestedProp: 'nestedValue',
},
}),
},
}));
import { mount } from 'enzyme';
import React from 'react';
import { UICapabilities } from '..';
import { injectUICapabilities } from './inject_ui_capabilities';
import { UICapabilitiesProvider } from './ui_capabilities_provider';
describe('injectUICapabilities', () => {
it('provides UICapabilities to FCs', () => {
interface FCProps {
uiCapabilities: UICapabilities;
}
const MyFC = injectUICapabilities(({ uiCapabilities }: FCProps) => {
return <span>{uiCapabilities.uiCapability2.nestedProp}</span>;
});
const wrapper = mount(
<UICapabilitiesProvider>
<MyFC />
</UICapabilitiesProvider>
);
expect(wrapper).toMatchInlineSnapshot(`
<UICapabilitiesProvider>
<InjectUICapabilities(Component)>
<Component
uiCapabilities={
Object {
"uiCapability1": true,
"uiCapability2": Object {
"nestedProp": "nestedValue",
},
}
}
>
<span>
nestedValue
</span>
</Component>
</InjectUICapabilities(Component)>
</UICapabilitiesProvider>
`);
});
it('provides UICapabilities to class components', () => {
interface ClassProps {
uiCapabilities: UICapabilities;
}
// eslint-disable-next-line react/prefer-stateless-function
class MyClassComponent extends React.Component<ClassProps, {}> {
public render() {
return <span>{this.props.uiCapabilities.uiCapability2.nestedProp}</span>;
}
}
const WrappedComponent = injectUICapabilities(MyClassComponent);
const wrapper = mount(
<UICapabilitiesProvider>
<WrappedComponent />
</UICapabilitiesProvider>
);
expect(wrapper).toMatchInlineSnapshot(`
<UICapabilitiesProvider>
<InjectUICapabilities(MyClassComponent)>
<MyClassComponent
uiCapabilities={
Object {
"uiCapability1": true,
"uiCapability2": Object {
"nestedProp": "nestedValue",
},
}
}
>
<span>
nestedValue
</span>
</MyClassComponent>
</InjectUICapabilities(MyClassComponent)>
</UICapabilitiesProvider>
`);
});
});

View file

@ -1,53 +0,0 @@
/*
* Licensed to Elasticsearch B.V. under one or more contributor
* license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright
* ownership. Elasticsearch B.V. licenses this file to you under
* the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
import React, { Component, ComponentClass, ComponentType } from 'react';
import { UICapabilitiesContext } from './ui_capabilities_context';
import { UICapabilities } from '..';
function getDisplayName(component: ComponentType<any>) {
return component.displayName || component.name || 'Component';
}
interface InjectedProps {
uiCapabilities: UICapabilities;
}
export function injectUICapabilities<P>(
WrappedComponent: ComponentType<P & InjectedProps>
): ComponentClass<Pick<P, Exclude<keyof P, keyof InjectedProps>>> & {
WrappedComponent: ComponentType<P & InjectedProps>;
} {
class InjectUICapabilities extends Component<P, any> {
public static displayName = `InjectUICapabilities(${getDisplayName(WrappedComponent)})`;
public static WrappedComponent: ComponentType<P & InjectedProps> = WrappedComponent;
public static contextType = UICapabilitiesContext;
constructor(props: any, context: any) {
super(props, context);
}
public render() {
return <WrappedComponent {...this.props} {...{ uiCapabilities: this.context }} />;
}
}
return InjectUICapabilities;
}

View file

@ -1,21 +0,0 @@
/*
* Licensed to Elasticsearch B.V. under one or more contributor
* license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright
* ownership. Elasticsearch B.V. licenses this file to you under
* the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
export { UICapabilitiesProvider } from './ui_capabilities_provider';
export { injectUICapabilities } from './inject_ui_capabilities';

View file

@ -1,116 +0,0 @@
/*
* Licensed to Elasticsearch B.V. under one or more contributor
* license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright
* ownership. Elasticsearch B.V. licenses this file to you under
* the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
jest.mock('ui/capabilities', () => ({
capabilities: {
get: () => ({
uiCapability1: true,
uiCapability2: {
nestedProp: 'nestedValue',
},
}),
},
}));
import { mount } from 'enzyme';
import React from 'react';
import { UICapabilities } from '../..';
import { injectUICapabilities } from './inject_ui_capabilities';
import { UICapabilitiesProvider } from './ui_capabilities_provider';
describe('injectUICapabilities', () => {
it('provides UICapabilities to FCs', () => {
interface FCProps {
uiCapabilities: UICapabilities;
}
const MyFC = injectUICapabilities(({ uiCapabilities }: FCProps) => {
return <span>{uiCapabilities.uiCapability2.nestedProp}</span>;
});
const wrapper = mount(
<UICapabilitiesProvider>
<MyFC />
</UICapabilitiesProvider>
);
expect(wrapper).toMatchInlineSnapshot(`
<UICapabilitiesProvider>
<InjectUICapabilities(Component)>
<Component
uiCapabilities={
Object {
"uiCapability1": true,
"uiCapability2": Object {
"nestedProp": "nestedValue",
},
}
}
>
<span>
nestedValue
</span>
</Component>
</InjectUICapabilities(Component)>
</UICapabilitiesProvider>
`);
});
it('provides UICapabilities to class components', () => {
interface ClassProps {
uiCapabilities: UICapabilities;
}
// eslint-disable-next-line react/prefer-stateless-function
class MyClassComponent extends React.Component<ClassProps, {}> {
public render() {
return <span>{this.props.uiCapabilities.uiCapability2.nestedProp}</span>;
}
}
const WrappedComponent = injectUICapabilities(MyClassComponent);
const wrapper = mount(
<UICapabilitiesProvider>
<WrappedComponent />
</UICapabilitiesProvider>
);
expect(wrapper).toMatchInlineSnapshot(`
<UICapabilitiesProvider>
<InjectUICapabilities(MyClassComponent)>
<MyClassComponent
uiCapabilities={
Object {
"uiCapability1": true,
"uiCapability2": Object {
"nestedProp": "nestedValue",
},
}
}
>
<span>
nestedValue
</span>
</MyClassComponent>
</InjectUICapabilities(MyClassComponent)>
</UICapabilitiesProvider>
`);
});
});

View file

@ -1,57 +0,0 @@
/*
* Licensed to Elasticsearch B.V. under one or more contributor
* license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright
* ownership. Elasticsearch B.V. licenses this file to you under
* the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
import PropTypes from 'prop-types';
import React, { Component, ComponentClass, ComponentType } from 'react';
import { UICapabilities } from '../..';
function getDisplayName(component: ComponentType<any>) {
return component.displayName || component.name || 'Component';
}
interface InjectedProps {
uiCapabilities: UICapabilities;
}
export function injectUICapabilities<P>(
WrappedComponent: ComponentType<P & InjectedProps>
): ComponentClass<Pick<P, Exclude<keyof P, keyof InjectedProps>>> & {
WrappedComponent: ComponentType<P & InjectedProps>;
} {
class InjectUICapabilities extends Component<P, any> {
public static displayName = `InjectUICapabilities(${getDisplayName(WrappedComponent)})`;
public static WrappedComponent: ComponentType<P & InjectedProps> = WrappedComponent;
public static contextTypes = {
uiCapabilities: PropTypes.object.isRequired,
};
constructor(props: any, context: any) {
super(props, context);
}
public render() {
return (
<WrappedComponent {...this.props} {...{ uiCapabilities: this.context.uiCapabilities }} />
);
}
}
return InjectUICapabilities;
}

View file

@ -1,48 +0,0 @@
/*
* Licensed to Elasticsearch B.V. under one or more contributor
* license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright
* ownership. Elasticsearch B.V. licenses this file to you under
* the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
import PropTypes from 'prop-types';
import React, { ReactNode } from 'react';
import { capabilities, UICapabilities } from '../..';
interface Props {
children: ReactNode;
}
interface ProviderContext {
uiCapabilities: UICapabilities;
}
export class UICapabilitiesProvider extends React.Component<Props, {}> {
public static displayName: string = 'UICapabilitiesProvider';
public static childContextTypes = {
uiCapabilities: PropTypes.object.isRequired,
};
public getChildContext(): ProviderContext {
return {
uiCapabilities: capabilities.get(),
};
}
public render() {
return React.Children.only(this.props.children);
}
}

View file

@ -1,27 +0,0 @@
/*
* Licensed to Elasticsearch B.V. under one or more contributor
* license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright
* ownership. Elasticsearch B.V. licenses this file to you under
* the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
import React from 'react';
import { UICapabilities } from '..';
export const UICapabilitiesContext = React.createContext<UICapabilities>({
navLinks: {},
catalogue: {},
management: {},
});

View file

@ -1,30 +0,0 @@
/*
* Licensed to Elasticsearch B.V. under one or more contributor
* license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright
* ownership. Elasticsearch B.V. licenses this file to you under
* the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
import React from 'react';
import { UICapabilitiesContext } from './ui_capabilities_context';
import { capabilities } from '..';
export const UICapabilitiesProvider: React.FC = (props) => (
<UICapabilitiesContext.Provider value={capabilities.get()}>
{props.children}
</UICapabilitiesContext.Provider>
);
UICapabilitiesProvider.displayName = 'UICapabilitiesProvider';

View file

@ -1,83 +0,0 @@
/*
* Licensed to Elasticsearch B.V. under one or more contributor
* license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright
* ownership. Elasticsearch B.V. licenses this file to you under
* the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
import { uiSettingsServiceMock } from '../../../../../core/public/mocks';
const uiSettingsClient = {
...uiSettingsServiceMock.createSetupContract(),
getUpdate$: () => ({
subscribe: jest.fn(),
}),
};
const chrome = {
addBasePath: (path) => (path ? path : 'test/base/path'),
breadcrumbs: {
set: () => ({}),
},
getBasePath: () => '/test/base/path',
getInjected: jest.fn(),
getUiSettingsClient: () => uiSettingsClient,
getSavedObjectsClient: () => '',
getXsrfToken: () => 'kbn-xsrf-token',
};
// eslint-disable-next-line import/no-default-export
export default chrome;
// Copied from `src/legacy/ui/public/chrome/chrome.js`
import _ from 'lodash';
import angular from 'angular';
import { metadata } from '../../metadata';
const internals = _.defaults(_.cloneDeep(metadata), {
basePath: '',
rootController: null,
rootTemplate: null,
showAppsLink: null,
xsrfToken: null,
devMode: true,
brand: null,
nav: [],
applicationClasses: [],
});
const waitForBootstrap = new Promise((resolve) => {
chrome.bootstrap = function (targetDomElement) {
// sets attribute on body for stylesheet sandboxing
document.body.setAttribute('id', `${internals.app.id}-app`);
chrome.setupAngular();
targetDomElement.setAttribute('kbn-chrome', 'true');
targetDomElement.setAttribute('ng-class', "{ 'hidden-chrome': !chrome.getVisible() }");
targetDomElement.className = 'app-wrapper';
angular.bootstrap(targetDomElement, ['kibana']);
resolve(targetDomElement);
};
});
chrome.dangerouslyGetActiveInjector = () => {
return waitForBootstrap.then((targetDomElement) => {
const $injector = angular.element(targetDomElement).injector();
if (!$injector) {
return Promise.reject('targetDomElement had no angular context after bootstrapping');
}
return $injector;
});
};

View file

@ -1,36 +0,0 @@
/*
* Licensed to Elasticsearch B.V. under one or more contributor
* license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright
* ownership. Elasticsearch B.V. licenses this file to you under
* the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
import { initAngularApi } from '../angular';
import { noop } from 'lodash';
describe('Chrome API :: Angular', () => {
describe('location helper methods', () => {
it('should return the sub app based on the url', () => {
const chrome = {
getInjected: noop,
addBasePath: noop,
};
initAngularApi(chrome, {
devMode: true,
});
});
it('should return breadcrumbs based on the url', () => {});
});
});

View file

@ -1,121 +0,0 @@
/*
* Licensed to Elasticsearch B.V. under one or more contributor
* license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright
* ownership. Elasticsearch B.V. licenses this file to you under
* the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
import expect from '@kbn/expect';
import setup from '../apps';
describe('Chrome API :: apps', function () {
describe('#get/setShowAppsLink()', function () {
describe('defaults to false if there are less than two apps', function () {
it('appCount = 0', function () {
const chrome = {};
setup(chrome, { nav: [] });
expect(chrome.getShowAppsLink()).to.equal(false);
});
it('appCount = 1', function () {
const chrome = {};
setup(chrome, { nav: [{ url: '/' }] });
expect(chrome.getShowAppsLink()).to.equal(false);
});
});
describe('defaults to true if there are two or more apps', function () {
it('appCount = 2', function () {
const chrome = {};
setup(chrome, { nav: [{ url: '/' }, { url: '/2' }] });
expect(chrome.getShowAppsLink()).to.equal(true);
});
it('appCount = 3', function () {
const chrome = {};
setup(chrome, { nav: [{ url: '/' }, { url: '/2' }, { url: '/3' }] });
expect(chrome.getShowAppsLink()).to.equal(true);
});
});
it('is chainable', function () {
const chrome = {};
setup(chrome, { nav: [{ url: '/' }] });
expect(chrome.setShowAppsLink(true)).to.equal(chrome);
});
it('can be changed', function () {
const chrome = {};
setup(chrome, { nav: [{ url: '/' }] });
expect(chrome.setShowAppsLink(true).getShowAppsLink()).to.equal(true);
expect(chrome.getShowAppsLink()).to.equal(true);
expect(chrome.setShowAppsLink(false).getShowAppsLink()).to.equal(false);
expect(chrome.getShowAppsLink()).to.equal(false);
});
});
describe('#getApp()', function () {
it('returns a clone of the current app', function () {
const chrome = {};
const app = { url: '/' };
setup(chrome, { app });
expect(chrome.getApp()).to.eql(app);
expect(chrome.getApp()).to.not.equal(app);
});
it('returns undefined if no active app', function () {
const chrome = {};
setup(chrome, {});
expect(chrome.getApp()).to.equal(undefined);
});
});
describe('#getAppTitle()', function () {
it('returns the title property of the current app', function () {
const chrome = {};
const app = { url: '/', title: 'foo' };
setup(chrome, { app });
expect(chrome.getAppTitle()).to.eql('foo');
});
it('returns undefined if no active app', function () {
const chrome = {};
setup(chrome, {});
expect(chrome.getAppTitle()).to.equal(undefined);
});
});
describe('#getAppUrl()', function () {
it('returns the resolved url of the current app', function () {
const chrome = {};
const app = { navLink: { url: '/foo' } };
setup(chrome, { app });
const a = document.createElement('a');
a.setAttribute('href', app.navLink.url);
expect(chrome.getAppUrl()).to.equal(a.href);
});
it('returns undefined if no active app', function () {
const chrome = {};
setup(chrome, {});
expect(chrome.getAppUrl()).to.equal(undefined);
});
});
});

View file

@ -1,143 +0,0 @@
/*
* Licensed to Elasticsearch B.V. under one or more contributor
* license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright
* ownership. Elasticsearch B.V. licenses this file to you under
* the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
import expect from '@kbn/expect';
import sinon from 'sinon';
import { initChromeNavApi } from '../nav';
import { StubBrowserStorage } from 'test_utils/stub_browser_storage';
import { npStart } from 'ui/new_platform';
import { absoluteToParsedUrl } from '../../../url/absolute_to_parsed_url';
const basePath = '/someBasePath';
function init(customInternals = { basePath }) {
const chrome = {
addBasePath: (path) => path,
getBasePath: () => customInternals.basePath || '',
};
const internals = {
nav: [],
...customInternals,
};
initChromeNavApi(chrome, internals);
return { chrome, internals };
}
describe('chrome nav apis', function () {
let coreNavLinks;
let fakedLinks = [];
const baseUrl = (function () {
const a = document.createElement('a');
a.setAttribute('href', '/');
return a.href.slice(0, a.href.length - 1);
})();
beforeEach(() => {
coreNavLinks = npStart.core.chrome.navLinks;
sinon.stub(coreNavLinks, 'update').callsFake((linkId, updateAttrs) => {
const link = fakedLinks.find(({ id }) => id === linkId);
for (const key of Object.keys(updateAttrs)) {
link[key] = updateAttrs[key];
}
return link;
});
sinon.stub(coreNavLinks, 'getAll').callsFake(() => fakedLinks);
sinon
.stub(coreNavLinks, 'get')
.callsFake((linkId) => fakedLinks.find(({ id }) => id === linkId));
});
afterEach(() => {
coreNavLinks.update.restore();
coreNavLinks.getAll.restore();
coreNavLinks.get.restore();
});
describe('#untrackNavLinksForDeletedSavedObjects', function () {
const appId = 'appId';
const appUrl = `${baseUrl}/app/kibana#test`;
const deletedId = 'IAMDELETED';
it('should clear last url when last url contains link to deleted saved object', function () {
const appUrlStore = new StubBrowserStorage();
fakedLinks = [
{
id: appId,
title: 'Discover',
url: `${appUrl}?id=${deletedId}`,
baseUrl: appUrl,
linkToLastSubUrl: true,
legacy: true,
},
];
const { chrome } = init({ appUrlStore });
chrome.untrackNavLinksForDeletedSavedObjects([deletedId]);
expect(coreNavLinks.update.calledWith(appId, { url: appUrl })).to.be(true);
});
it('should not clear last url when last url does not contains link to deleted saved object', function () {
const lastUrl = `${appUrl}?id=anotherSavedObjectId`;
const appUrlStore = new StubBrowserStorage();
fakedLinks = [
{
id: appId,
title: 'Discover',
url: lastUrl,
baseUrl: appUrl,
linkToLastSubUrl: true,
legacy: true,
},
];
const { chrome } = init({ appUrlStore });
chrome.untrackNavLinksForDeletedSavedObjects([deletedId]);
expect(coreNavLinks.update.calledWith(appId, { url: appUrl })).to.be(false);
});
});
describe('chrome.trackSubUrlForApp()', function () {
it('injects a manual app url', function () {
const appUrlStore = new StubBrowserStorage();
fakedLinks = [
{
id: 'visualize',
baseUrl: `${baseUrl}/app/visualize#`,
url: `${baseUrl}/app/visualize#`,
subUrlBase: '/app/visualize#',
legacy: true,
},
];
const { chrome } = init({ appUrlStore });
const kibanaParsedUrl = absoluteToParsedUrl(
`${baseUrl}/xyz/app/visualize#/1234?_g=globalstate`,
'/xyz'
);
chrome.trackSubUrlForApp('visualize', kibanaParsedUrl);
expect(
coreNavLinks.update.calledWith('visualize', {
url: `${baseUrl}/xyz/app/visualize#/1234?_g=globalstate`,
})
).to.be(true);
});
});
});

View file

@ -1,130 +0,0 @@
/*
* Licensed to Elasticsearch B.V. under one or more contributor
* license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright
* ownership. Elasticsearch B.V. licenses this file to you under
* the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
import ngMock from 'ng_mock';
import expect from '@kbn/expect';
import { SubUrlRouteFilterProvider } from '../sub_url_hooks';
describe('kbn-chrome subUrlRouteFilter()', () => {
describe('no ngRoute', () => {
beforeEach(ngMock.module('kibana/private'));
beforeEach(
ngMock.inject(($injector) => {
expect($injector.has('$route')).to.be(false);
})
);
it(
'always returns true when there is no $route service',
ngMock.inject((Private) => {
const subUrlRouteFilter = Private(SubUrlRouteFilterProvider);
expect(subUrlRouteFilter()).to.be(true);
})
);
});
describe('with ngRoute', () => {
beforeEach(
ngMock.module('kibana/private', 'ngRoute', ($routeProvider) => {
$routeProvider.when('/foo', {
redirectTo: '/bar',
});
$routeProvider.when('/bar', {
template: '<div>foo => bar</div>',
});
})
);
let test;
beforeEach(
ngMock.inject((Private, $location, $rootScope, $route) => {
test = ({ path, assert }) => {
const subUrlRouteFilter = Private(SubUrlRouteFilterProvider);
$location.path(path);
let result;
function runAssert() {
if (result) {
// only run once
return;
}
try {
assert($route, subUrlRouteFilter);
result = {};
} catch (error) {
result = { error };
}
}
$rootScope.$on('$routeUpdate', runAssert);
$rootScope.$on('$routeChangeSuccess', runAssert);
$rootScope.$apply();
// when no route matches there is no event so we run manually
if (!result) {
runAssert();
}
if (result.error) {
throw result.error;
}
};
})
);
describe('no current route', () => {
it('returns false', () => {
test({
path: '/baz',
assert($route, subUrlRouteFilter) {
expect($route.current).to.not.be.ok();
expect(subUrlRouteFilter()).to.eql(false);
},
});
});
});
describe('redirectTo route', () => {
it('is called on target route', () => {
test({
path: '/foo',
assert($route) {
expect($route.current.$$route.originalPath).to.be('/bar');
},
});
});
});
describe('standard route', () => {
it('returns true', () => {
test({
path: '/bar',
assert($route, subUrlRouteFilter) {
expect($route.current).to.be.ok();
expect($route.current.template).to.be.ok();
expect(subUrlRouteFilter()).to.eql(true);
},
});
});
});
});
});

View file

@ -1,41 +0,0 @@
/*
* Licensed to Elasticsearch B.V. under one or more contributor
* license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright
* ownership. Elasticsearch B.V. licenses this file to you under
* the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
import expect from '@kbn/expect';
import sinon from 'sinon';
import { initChromeXsrfApi } from '../xsrf';
// eslint-disable-next-line @kbn/eslint/no-restricted-paths
import { version } from '../../../../../../core/server/utils/package_json';
describe('chrome xsrf apis', function () {
const sandbox = sinon.createSandbox();
afterEach(function () {
sandbox.restore();
});
describe('#getXsrfToken()', function () {
it('exposes the token', function () {
const chrome = {};
initChromeXsrfApi(chrome, { version });
expect(chrome.getXsrfToken()).to.be(version);
});
});
});

View file

@ -1,40 +0,0 @@
/*
* Licensed to Elasticsearch B.V. under one or more contributor
* license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright
* ownership. Elasticsearch B.V. licenses this file to you under
* the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
import { uiModules } from '../../modules';
import { directivesProvider } from '../directives';
import { registerSubUrlHooks } from './sub_url_hooks';
import { configureAppAngularModule } from 'ui/legacy_compat';
import { npStart } from '../../new_platform/new_platform';
export function initAngularApi(chrome, internals) {
chrome.setupAngular = function () {
const kibana = uiModules.get('kibana');
configureAppAngularModule(kibana, npStart.core, false);
kibana.value('chrome', chrome);
registerSubUrlHooks(kibana, internals);
directivesProvider(chrome, internals);
uiModules.link(kibana);
};
}

View file

@ -1,83 +0,0 @@
/*
* Licensed to Elasticsearch B.V. under one or more contributor
* license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright
* ownership. Elasticsearch B.V. licenses this file to you under
* the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
import { clone, get } from 'lodash';
import { resolve } from 'url';
// eslint-disable-next-line import/no-default-export
export default function (chrome, internals) {
if (get(internals, 'app.navLink.url')) {
internals.app.navLink.url = resolve(window.location.href, internals.app.navLink.url);
}
internals.appUrlStore = internals.appUrlStore || window.sessionStorage;
try {
const verifySessionStorage = 'verify sessionStorage';
internals.appUrlStore.setItem(verifySessionStorage, 1);
internals.appUrlStore.removeItem(verifySessionStorage);
} catch (error) {
throw new Error(
'Kibana requires access to sessionStorage, and it looks like ' +
"your browser is restricting it. If you're " +
'using Safari with private browsing enabled, you can solve this ' +
'problem by disabling private browsing, or by using another browser.'
);
}
/**
* ui/chrome apps API
*
* ui/chrome has some metadata about the current app, and enables the
* navbar link, a small grid to the left of the tabs, when there is more
* than one app installed.
*/
chrome.setShowAppsLink = function (val) {
internals.showAppsLink = !!val;
return chrome;
};
chrome.getShowAppsLink = function () {
return internals.showAppsLink == null ? internals.nav.length > 1 : internals.showAppsLink;
};
chrome.getKibanaVersion = function () {
return internals.version;
};
chrome.getApp = function () {
return clone(internals.app);
};
chrome.getAppTitle = function () {
return get(internals, ['app', 'title']);
};
chrome.getAppUrl = function () {
return get(internals, ['app', 'navLink', 'url']);
};
chrome.getLastUrlFor = function (appId) {
return internals.appUrlStore.getItem(`appLastUrl:${appId}`);
};
chrome.setLastUrlFor = function (appId, url) {
internals.appUrlStore.setItem(`appLastUrl:${appId}`, url);
};
}

View file

@ -1,27 +0,0 @@
/*
* Licensed to Elasticsearch B.V. under one or more contributor
* license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright
* ownership. Elasticsearch B.V. licenses this file to you under
* the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
import { chromeServiceMock } from '../../../../../core/public/mocks';
export const newPlatformChrome = chromeServiceMock.createStartContract();
jest.doMock('ui/new_platform', () => ({
npStart: {
core: { chrome: newPlatformChrome },
},
}));

View file

@ -1,61 +0,0 @@
/*
* Licensed to Elasticsearch B.V. under one or more contributor
* license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright
* ownership. Elasticsearch B.V. licenses this file to you under
* the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
import * as Rx from 'rxjs';
// eslint-disable-next-line @kbn/eslint/no-restricted-paths
import { ChromeBadge } from 'src/core/public/chrome';
import { newPlatformChrome } from './badge.test.mocks';
import { initChromeBadgeApi } from './badge';
function setup() {
const getBadge$ = new Rx.BehaviorSubject<ChromeBadge | undefined>(undefined);
newPlatformChrome.getBadge$.mockReturnValue(getBadge$);
const chrome: any = {};
initChromeBadgeApi(chrome);
return { chrome, getBadge$ };
}
afterEach(() => {
jest.resetAllMocks();
});
describe('badge', () => {
describe('#get$()', () => {
it('returns newPlatformChrome.getBadge$()', () => {
const { chrome } = setup();
expect(chrome.badge.get$()).toBe(newPlatformChrome.getBadge$());
});
});
describe('#set()', () => {
it('calls newPlatformChrome.setBadge', () => {
const { chrome } = setup();
const badge = {
text: 'foo',
tooltip: `foo's tooltip`,
iconType: 'alert',
};
chrome.badge.set(badge);
expect(newPlatformChrome.setBadge).toHaveBeenCalledTimes(1);
expect(newPlatformChrome.setBadge).toHaveBeenCalledWith(badge);
});
});
});

View file

@ -1,53 +0,0 @@
/*
* Licensed to Elasticsearch B.V. under one or more contributor
* license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright
* ownership. Elasticsearch B.V. licenses this file to you under
* the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
import { Chrome } from 'ui/chrome';
import { npStart } from 'ui/new_platform';
import { ChromeBadge } from '../../../../../core/public';
export type Badge = ChromeBadge;
export type BadgeApi = ReturnType<typeof createBadgeApi>['badge'];
const newPlatformChrome = npStart.core.chrome;
function createBadgeApi() {
return {
badge: {
/**
* Get an observable that emits the current badge
* and emits each update to the badge
*/
get$() {
return newPlatformChrome.getBadge$();
},
/**
* Replace the badge with a new one
*/
set(newBadge?: Badge) {
newPlatformChrome.setBadge(newBadge);
},
},
};
}
export function initChromeBadgeApi(chrome: Chrome) {
const { badge } = createBadgeApi();
chrome.badge = badge;
}

View file

@ -1,27 +0,0 @@
/*
* Licensed to Elasticsearch B.V. under one or more contributor
* license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright
* ownership. Elasticsearch B.V. licenses this file to you under
* the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
import { httpServiceMock } from '../../../../../core/public/mocks';
const newPlatformHttp = httpServiceMock.createSetupContract({ basePath: 'npBasePath' });
jest.doMock('ui/new_platform', () => ({
npSetup: {
core: { http: newPlatformHttp },
},
}));

View file

@ -1,51 +0,0 @@
/*
* Licensed to Elasticsearch B.V. under one or more contributor
* license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright
* ownership. Elasticsearch B.V. licenses this file to you under
* the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
import './base_path.test.mocks';
import { initChromeBasePathApi } from './base_path';
function initChrome() {
const chrome: any = {};
initChromeBasePathApi(chrome);
return chrome;
}
beforeEach(() => {
jest.clearAllMocks();
});
describe('#getBasePath()', () => {
it('proxies to newPlatformHttp.basePath.get()', () => {
const chrome = initChrome();
expect(chrome.getBasePath()).toBe('npBasePath');
});
});
describe('#addBasePath()', () => {
it('proxies to newPlatformHttp.basePath.prepend(path)', () => {
const chrome = initChrome();
expect(chrome.addBasePath('/foo/bar')).toBe('npBasePath/foo/bar');
});
});
describe('#removeBasePath', () => {
it('proxies to newPlatformBasePath.basePath.remove(path)', () => {
const chrome = initChrome();
expect(chrome.removeBasePath('npBasePath/foo/bar')).toBe('/foo/bar');
});
});

View file

@ -1,28 +0,0 @@
/*
* Licensed to Elasticsearch B.V. under one or more contributor
* license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright
* ownership. Elasticsearch B.V. licenses this file to you under
* the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
import { npSetup } from 'ui/new_platform';
const newPlatformHttp = npSetup.core.http;
export function initChromeBasePathApi(chrome: { [key: string]: any }) {
chrome.getBasePath = newPlatformHttp.basePath.get;
chrome.addBasePath = newPlatformHttp.basePath.prepend;
chrome.removeBasePath = newPlatformHttp.basePath.remove;
}

View file

@ -1,86 +0,0 @@
/*
* Licensed to Elasticsearch B.V. under one or more contributor
* license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright
* ownership. Elasticsearch B.V. licenses this file to you under
* the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
import { npStart } from 'ui/new_platform';
import { ChromeBreadcrumb } from '../../../../../core/public';
export type Breadcrumb = ChromeBreadcrumb;
export type BreadcrumbsApi = ReturnType<typeof createBreadcrumbsApi>['breadcrumbs'];
const newPlatformChrome = npStart.core.chrome;
function createBreadcrumbsApi(chrome: { [key: string]: any }) {
let currentBreadcrumbs: Breadcrumb[] = [];
// reset breadcrumbSetSinceRouteChange any time the breadcrumbs change, even
// if it was done directly through the new platform
newPlatformChrome.getBreadcrumbs$().subscribe({
next(nextBreadcrumbs) {
currentBreadcrumbs = nextBreadcrumbs;
},
});
return {
breadcrumbs: {
/**
* Get an observerable that emits the current list of breadcrumbs
* and emits each update to the breadcrumbs
*/
get$() {
return newPlatformChrome.getBreadcrumbs$();
},
/**
* Replace the set of breadcrumbs with a new set
*/
set(newBreadcrumbs: Breadcrumb[]) {
newPlatformChrome.setBreadcrumbs(newBreadcrumbs);
},
/**
* Add a breadcrumb to the end of the list of breadcrumbs
*/
push(breadcrumb: Breadcrumb) {
newPlatformChrome.setBreadcrumbs([...currentBreadcrumbs, breadcrumb]);
},
/**
* Filter the current set of breadcrumbs with a function. Works like Array#filter()
*/
filter(fn: (breadcrumb: Breadcrumb, i: number, all: Breadcrumb[]) => boolean) {
newPlatformChrome.setBreadcrumbs(currentBreadcrumbs.filter(fn));
},
/**
* Remove last element of the breadcrumb
*/
pop() {
newPlatformChrome.setBreadcrumbs(currentBreadcrumbs.slice(0, -1));
},
},
};
}
export function initBreadcrumbsApi(
chrome: { [key: string]: any },
internals: { [key: string]: any }
) {
const { breadcrumbs } = createBreadcrumbsApi(chrome);
chrome.breadcrumbs = breadcrumbs;
}

View file

@ -1,27 +0,0 @@
/*
* Licensed to Elasticsearch B.V. under one or more contributor
* license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright
* ownership. Elasticsearch B.V. licenses this file to you under
* the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
import { chromeServiceMock } from '../../../../../core/public/mocks';
export const newPlatformChrome = chromeServiceMock.createStartContract();
jest.doMock('ui/new_platform', () => ({
npStart: {
core: { chrome: newPlatformChrome },
},
}));

View file

@ -1,68 +0,0 @@
/*
* Licensed to Elasticsearch B.V. under one or more contributor
* license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright
* ownership. Elasticsearch B.V. licenses this file to you under
* the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
import * as Rx from 'rxjs';
import { newPlatformChrome } from './controls.test.mocks';
import { initChromeControlsApi } from './controls';
function setup() {
const isVisible$ = new Rx.BehaviorSubject(true);
newPlatformChrome.getIsVisible$.mockReturnValue(isVisible$);
const chrome: any = {};
initChromeControlsApi(chrome);
return { chrome, isVisible$ };
}
afterEach(() => {
jest.resetAllMocks();
});
describe('setVisible', () => {
it('passes the visibility to the newPlatform', () => {
const { chrome } = setup();
chrome.setVisible(true);
chrome.setVisible(false);
chrome.setVisible(false);
expect(newPlatformChrome.setIsVisible.mock.calls).toMatchInlineSnapshot(`
Array [
Array [
true,
],
Array [
false,
],
Array [
false,
],
]
`);
});
});
describe('getVisible', () => {
it('returns a the cached value emitted by the newPlatformChrome', () => {
const { chrome, isVisible$ } = setup();
isVisible$.next(true);
expect(chrome.getVisible()).toBe(true);
isVisible$.next(false);
expect(chrome.getVisible()).toBe(false);
});
});

View file

@ -1,47 +0,0 @@
/*
* Licensed to Elasticsearch B.V. under one or more contributor
* license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright
* ownership. Elasticsearch B.V. licenses this file to you under
* the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
import * as Rx from 'rxjs';
import { npStart } from 'ui/new_platform';
const newPlatformChrome = npStart.core.chrome;
export function initChromeControlsApi(chrome: { [key: string]: any }) {
// cache of chrome visibility state
const visible$ = new Rx.BehaviorSubject(false);
newPlatformChrome.getIsVisible$().subscribe(visible$);
/**
* Set the temporary visibility for the chrome. This does nothing if the chrome is hidden
* by default and should be used to hide the chrome for things like full-screen modes
* with an exit button.
*/
chrome.setVisible = (visibility: boolean) => {
newPlatformChrome.setIsVisible(visibility);
return chrome;
};
/**
* Get the current visibility state of the chrome. Note that this drives the UI so it
* might be incorrect in the moments just before the UI is updated.
*/
chrome.getVisible = () => visible$.getValue();
chrome.visible$ = visible$.asObservable();
}

View file

@ -1,53 +0,0 @@
/*
* Licensed to Elasticsearch B.V. under one or more contributor
* license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright
* ownership. Elasticsearch B.V. licenses this file to you under
* the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
import { npStart } from 'ui/new_platform';
import { ChromeHelpExtension } from '../../../../../core/public';
const newPlatformChrome = npStart.core.chrome;
export type HelpExtensionApi = ReturnType<typeof createHelpExtensionApi>['helpExtension'];
export type HelpExtension = ChromeHelpExtension;
function createHelpExtensionApi() {
return {
helpExtension: {
/**
* Set the custom help extension, or clear it by passing undefined. This
* will be rendered within the help popover in the header
*/
set: (helpExtension: HelpExtension | undefined) => {
newPlatformChrome.setHelpExtension(helpExtension);
},
/**
* Get the current help extension that should be rendered in the header
*/
get$: () => newPlatformChrome.getHelpExtension$(),
},
};
}
export function initHelpExtensionApi(
chrome: { [key: string]: any },
internal: { [key: string]: any }
) {
const { helpExtension } = createHelpExtensionApi();
chrome.helpExtension = helpExtension;
}

View file

@ -1,28 +0,0 @@
/*
* Licensed to Elasticsearch B.V. under one or more contributor
* license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright
* ownership. Elasticsearch B.V. licenses this file to you under
* the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
export const newPlatformInjectedMetadata: any = {
getInjectedVars: jest.fn(),
getInjectedVar: jest.fn(),
};
jest.doMock('ui/new_platform', () => ({
npSetup: {
core: { injectedMetadata: newPlatformInjectedMetadata },
},
}));

View file

@ -1,78 +0,0 @@
/*
* Licensed to Elasticsearch B.V. under one or more contributor
* license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright
* ownership. Elasticsearch B.V. licenses this file to you under
* the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
import { newPlatformInjectedMetadata } from './injected_vars.test.mocks';
import { initChromeInjectedVarsApi } from './injected_vars';
function initChrome() {
const chrome: any = {};
initChromeInjectedVarsApi(chrome);
return chrome;
}
beforeEach(() => {
jest.resetAllMocks();
});
describe('#getInjected()', () => {
it('proxies to newPlatformInjectedMetadata service', () => {
const chrome = initChrome();
chrome.getInjected();
chrome.getInjected('foo');
chrome.getInjected('foo', 'bar');
expect(newPlatformInjectedMetadata.getInjectedVars.mock.calls).toMatchInlineSnapshot(`
Array [
Array [],
]
`);
expect(newPlatformInjectedMetadata.getInjectedVar.mock.calls).toMatchInlineSnapshot(`
Array [
Array [
"foo",
undefined,
],
Array [
"foo",
"bar",
],
]
`);
});
it('returns mutable values, but does not persist changes internally', () => {
const chrome = initChrome();
newPlatformInjectedMetadata.getInjectedVars.mockReturnValue(
Object.freeze({
foo: Object.freeze({
bar: Object.freeze({
baz: 1,
}),
}),
})
);
const vars = chrome.getInjected();
expect(() => {
vars.newProperty = true;
}).not.toThrowError();
expect(chrome.getInjected()).not.toHaveProperty('newProperty');
});
});

View file

@ -1,32 +0,0 @@
/*
* Licensed to Elasticsearch B.V. under one or more contributor
* license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright
* ownership. Elasticsearch B.V. licenses this file to you under
* the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
import { cloneDeep } from 'lodash';
import { npSetup } from 'ui/new_platform';
const newPlatformInjectedVars = npSetup.core.injectedMetadata;
export function initChromeInjectedVarsApi(chrome: { [key: string]: any }) {
chrome.getInjected = (name?: string, defaultValue?: any) =>
cloneDeep(
name
? newPlatformInjectedVars.getInjectedVar(name, defaultValue)
: newPlatformInjectedVars.getInjectedVars()
);
}

View file

@ -1,65 +0,0 @@
/*
* Licensed to Elasticsearch B.V. under one or more contributor
* license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright
* ownership. Elasticsearch B.V. licenses this file to you under
* the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
import * as Rx from 'rxjs';
import { npSetup } from 'ui/new_platform';
const newPlatformHttp = npSetup.core.http;
export function initLoadingCountApi(chrome) {
const manualCount$ = new Rx.BehaviorSubject(0);
newPlatformHttp.addLoadingCountSource(manualCount$);
chrome.loadingCount = new (class ChromeLoadingCountApi {
/**
* Call to add a subscriber to for the loading count that
* will be called every time the loading count changes.
*
* @type {Observable<number>}
* @return {Function} unsubscribe
*/
subscribe(handler) {
const subscription = newPlatformHttp.getLoadingCount$().subscribe({
next(count) {
handler(count);
},
});
return () => {
subscription.unsubscribe();
};
}
/**
* Increment the loading count by one
* @return {undefined}
*/
increment() {
manualCount$.next(manualCount$.getValue() + 1);
}
/**
* Decrement the loading count by one
* @return {undefined}
*/
decrement() {
manualCount$.next(manualCount$.getValue() - 1);
}
})();
}

View file

@ -1,157 +0,0 @@
/*
* Licensed to Elasticsearch B.V. under one or more contributor
* license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright
* ownership. Elasticsearch B.V. licenses this file to you under
* the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
import { KibanaParsedUrl } from 'ui/url/kibana_parsed_url';
import { absoluteToParsedUrl } from '../../url/absolute_to_parsed_url';
import { npStart } from '../../new_platform';
import { ChromeNavLink } from '../../../../../core/public';
import { relativeToAbsolute } from '../../url/relative_to_absolute';
export interface ChromeNavLinks {
untrackNavLinksForDeletedSavedObjects(deletedIds: string[]): void;
trackSubUrlForApp(linkId: string, parsedKibanaUrl: KibanaParsedUrl): void;
}
interface NavInternals {
appUrlStore: Storage;
trackPossibleSubUrl(url: string): void;
}
export function initChromeNavApi(chrome: any, internals: NavInternals) {
const coreNavLinks = npStart.core.chrome.navLinks;
/**
* Clear last url for deleted saved objects to avoid loading pages with "Could not locate..."
*/
chrome.untrackNavLinksForDeletedSavedObjects = (deletedIds: string[]) => {
function urlContainsDeletedId(url: string) {
const includedId = deletedIds.find((deletedId) => {
return url.includes(deletedId);
});
return includedId !== undefined;
}
coreNavLinks.getAll().forEach((link) => {
if (link.linkToLastSubUrl && urlContainsDeletedId(link.url!)) {
setLastUrl(link, link.baseUrl);
}
});
};
/**
* Manually sets the last url for the given app. The last url for a given app is updated automatically during
* normal page navigation, so this should only need to be called to insert a last url that was not actually
* navigated to. For instance, when saving an object and redirecting to another page, the last url of the app
* should be the saved instance, but because of the redirect to a different page (e.g. `Save and Add to Dashboard`
* on visualize tab), it won't be tracked automatically and will need to be inserted manually. See
* https://github.com/elastic/kibana/pull/11932 for more background on why this was added.
*
* @param id {String} - an id that represents the navigation link.
* @param kibanaParsedUrl {KibanaParsedUrl} the url to track
*/
chrome.trackSubUrlForApp = (id: string, kibanaParsedUrl: KibanaParsedUrl) => {
const navLink = coreNavLinks.get(id);
if (navLink) {
setLastUrl(navLink, kibanaParsedUrl.getAbsoluteUrl());
}
};
internals.trackPossibleSubUrl = async function (url: string) {
const kibanaParsedUrl = absoluteToParsedUrl(url, chrome.getBasePath());
coreNavLinks
.getAll()
// Filter only legacy links
.filter((link) => link.legacy && !link.disableSubUrlTracking)
.forEach((link) => {
const active = url.startsWith(link.subUrlBase!);
link = coreNavLinks.update(link.id, { active })!;
if (active) {
setLastUrl(link, url);
return;
}
link = refreshLastUrl(link);
const newGlobalState = kibanaParsedUrl.getGlobalState();
if (newGlobalState) {
injectNewGlobalState(link, kibanaParsedUrl.appId, newGlobalState);
}
});
};
function lastSubUrlKey(link: ChromeNavLink) {
return `lastSubUrl:${link.baseUrl}`;
}
function getLastUrl(link: ChromeNavLink) {
return internals.appUrlStore.getItem(lastSubUrlKey(link));
}
function setLastUrl(link: ChromeNavLink, url: string) {
if (link.linkToLastSubUrl === false) {
return;
}
internals.appUrlStore.setItem(lastSubUrlKey(link), url);
refreshLastUrl(link);
}
function refreshLastUrl(link: ChromeNavLink) {
const lastSubUrl = getLastUrl(link);
return coreNavLinks.update(link.id, {
url: lastSubUrl || link.url || link.baseUrl,
})!;
}
function injectNewGlobalState(
link: ChromeNavLink,
fromAppId: string,
newGlobalState: string | string[]
) {
const kibanaParsedUrl = absoluteToParsedUrl(
getLastUrl(link) || link.url || link.baseUrl,
chrome.getBasePath()
);
// don't copy global state if links are for different apps
if (fromAppId !== kibanaParsedUrl.appId) return;
kibanaParsedUrl.setGlobalState(newGlobalState);
coreNavLinks.update(link.id, {
url: kibanaParsedUrl.getAbsoluteUrl(),
});
}
// simulate a possible change in url to initialize the
// link.active and link.lastUrl properties
coreNavLinks
.getAll()
.filter((link) => link.subUrlBase && !link.disableSubUrlTracking)
.forEach((link) => {
coreNavLinks.update(link.id, {
subUrlBase: relativeToAbsolute(chrome.addBasePath(link.subUrlBase)),
});
});
internals.trackPossibleSubUrl(document.location.href);
}

View file

@ -1,29 +0,0 @@
/*
* Licensed to Elasticsearch B.V. under one or more contributor
* license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright
* ownership. Elasticsearch B.V. licenses this file to you under
* the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
import { npStart } from 'ui/new_platform';
import { Chrome } from '..';
const savedObjectsClient = npStart.core.savedObjects.client;
export function initSavedObjectClient(chrome: Chrome) {
chrome.getSavedObjectsClient = function () {
return savedObjectsClient;
};
}

View file

@ -1,88 +0,0 @@
/*
* Licensed to Elasticsearch B.V. under one or more contributor
* license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright
* ownership. Elasticsearch B.V. licenses this file to you under
* the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
import { unhashUrl } from '../../../../../plugins/kibana_utils/public';
import { toastNotifications } from '../../notify/toasts';
import { npSetup } from '../../new_platform';
import { areHashesDifferentButDecodedHashesEquals } from './sub_url_hooks_utils';
export function registerSubUrlHooks(angularModule, internals) {
angularModule.run(($rootScope, Private, $location) => {
const subUrlRouteFilter = Private(SubUrlRouteFilterProvider);
function updateSubUrls() {
const urlWithHashes = window.location.href;
let urlWithStates;
try {
urlWithStates = unhashUrl(urlWithHashes);
} catch (e) {
toastNotifications.addDanger(e.message);
}
internals.trackPossibleSubUrl(urlWithStates || urlWithHashes);
}
function onRouteChange($event) {
if (subUrlRouteFilter($event)) {
updateUsage($event);
updateSubUrls();
}
}
$rootScope.$on('$locationChangeStart', (e, newUrl) => {
// This handler fixes issue #31238 where browser back navigation
// fails due to angular 1.6 parsing url encoded params wrong.
if (areHashesDifferentButDecodedHashesEquals($location.absUrl(), newUrl)) {
// replace the urlencoded hash with the version that angular sees.
const newHash = newUrl.split('#')[1] || '';
$location.url(newHash).replace();
}
});
$rootScope.$on('$routeChangeSuccess', onRouteChange);
$rootScope.$on('$routeUpdate', onRouteChange);
updateSubUrls();
});
}
function updateUsage($event) {
const scope = $event.targetScope;
const app = scope.chrome.getApp();
const appId = app.id === 'kibana' ? scope.getFirstPathSegment() : app.id;
if (npSetup.plugins.usageCollection) npSetup.plugins.usageCollection.__LEGACY.appChanged(appId);
}
/**
* Creates a function that will be called on each route change
* to determine if the event should be used to update the last
* subUrl of chrome links/tabs
* @injected
*/
export function SubUrlRouteFilterProvider($injector) {
if (!$injector.has('$route')) {
return function alwaysUpdate() {
return true;
};
}
const $route = $injector.get('$route');
return function ignoreRedirectToRoutes() {
return Boolean($route.current && !$route.current.redirectTo);
};
}

View file

@ -1,58 +0,0 @@
/*
* Licensed to Elasticsearch B.V. under one or more contributor
* license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright
* ownership. Elasticsearch B.V. licenses this file to you under
* the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
import { areHashesDifferentButDecodedHashesEquals } from './sub_url_hooks_utils';
test('false for different hashes', () => {
const url1 = `https://localhost/kibana/#/dashboard/id`;
const url2 = `https://localhost/kibana/#/dashboard/DIFFERENT`;
expect(areHashesDifferentButDecodedHashesEquals(url1, url2)).toBeFalsy();
});
test('false for same hashes', () => {
const hash = `/dashboard/id?_a=(filters:!(),query:(language:kuery,query:''))&_g=(filters:!(),time:(from:now-120m,to:now))`;
const url1 = `https://localhost/kibana/#/${hash}`;
expect(areHashesDifferentButDecodedHashesEquals(url1, url1)).toBeFalsy();
});
test('true for same hashes, but one is encoded', () => {
const hash = `/dashboard/id?_a=(filters:!(),query:(language:kuery,query:''))&_g=(filters:!(),time:(from:now-120m,to:now))`;
const url1 = `https://localhost/kibana/#/${hash}`;
const url2 = `https://localhost/kibana/#/${encodeURIComponent(hash)}`;
expect(areHashesDifferentButDecodedHashesEquals(url1, url2)).toBeTruthy();
});
/**
* This edge case occurs when trying to navigate within kibana app using core's `navigateToApp` api
* and there is reserved characters in hash (see: query:'' part)
* For example:
* ```ts
* navigateToApp('kibana', {
* path: '#/dashboard/f8bc19f0-6918-11ea-9258-a74c2ded064d?_a=(filters:!(),query:(language:kuery,query:''))&_g=(filters:!(),time:(from:now-120m,to:now))'
* })
* ```
* Core internally is using url.parse which parses ' -> %27 and performs the navigation
* Then angular decodes it back and causes redundant history record if not the fix which is covered by the test below
*/
test("true for same hashes, but one has reserved character (') encoded", () => {
const hash = `/dashboard/id?_a=(filters:!(),query:(language:kuery,query:''))&_g=(filters:!(),time:(from:now-120m,to:now))`;
const url1 = `https://localhost/kibana/#/${hash}`;
const url2 = `https://localhost/kibana/#/${hash.replace(/\'/g, '%27')}`;
expect(areHashesDifferentButDecodedHashesEquals(url1, url2)).toBeTruthy();
});

View file

@ -1,29 +0,0 @@
/*
* Licensed to Elasticsearch B.V. under one or more contributor
* license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright
* ownership. Elasticsearch B.V. licenses this file to you under
* the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
export function areHashesDifferentButDecodedHashesEquals(urlA: string, urlB: string): boolean {
const getHash = (url: string) => url.split('#')[1] ?? '';
const hashA = getHash(urlA);
const decodedHashA = decodeURIComponent(hashA);
const hashB = getHash(urlB);
const decodedHashB = decodeURIComponent(hashB);
return hashA !== hashB && decodedHashA === decodedHashB;
}

View file

@ -1,75 +0,0 @@
/*
* Licensed to Elasticsearch B.V. under one or more contributor
* license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright
* ownership. Elasticsearch B.V. licenses this file to you under
* the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
// eslint-disable-next-line import/no-default-export
export default function (chrome, internals) {
/**
* ui/chrome Template API
*
* Root Template
* The root template is rendered within the primary chrome ui and should
* be used when building an app that is more of a page, or to override the
* placement of ng-view. When including a root template, the mark-up will
* look something like this:
*
* body
* notifs
* div.content
* nav
* config
* div.application
* <-- your template here -->
*
* Root Controller
* To attach a controller to the root of ui/chrome's content area, outside of
* where it attaches the ng-view directive (assuming no rootTemplate is used)
* which will allow cause the controller to persist across views or make for
* a simple place to define some quick global functionality for a very simple
* app (like the status page).
*/
/**
* @param {string} template
* @return {chrome}
*/
chrome.setRootTemplate = function (template) {
internals.rootTemplate = template;
return chrome;
};
/**
* @param {string} as - the name that the controller should bind to
* @param {Function} controller - the controller initializer function
* @return {chrome}
*/
chrome.setRootController = function (as, controllerName) {
if (controllerName === undefined) {
controllerName = as;
as = null;
}
if (typeof controllerName === 'function') {
chrome.$$rootControllerConstruct = controllerName;
controllerName = 'chrome.$$rootControllerConstruct';
}
internals.rootController = controllerName + (as ? ' as ' + as : '');
return chrome;
};
}

View file

@ -1,27 +0,0 @@
/*
* Licensed to Elasticsearch B.V. under one or more contributor
* license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright
* ownership. Elasticsearch B.V. licenses this file to you under
* the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
import { chromeServiceMock } from '../../../../../core/public/mocks';
export const newPlatformChrome = chromeServiceMock.createStartContract();
jest.doMock('ui/new_platform', () => ({
npStart: {
core: { chrome: newPlatformChrome },
},
}));

View file

@ -1,144 +0,0 @@
/*
* Licensed to Elasticsearch B.V. under one or more contributor
* license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright
* ownership. Elasticsearch B.V. licenses this file to you under
* the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
import * as Rx from 'rxjs';
import { newPlatformChrome } from './theme.test.mocks';
import { initChromeThemeApi } from './theme';
function setup() {
const brand$ = new Rx.BehaviorSubject({ logo: 'foo', smallLogo: 'foo' });
newPlatformChrome.getBrand$.mockReturnValue(brand$);
const applicationClasses$ = new Rx.BehaviorSubject([] as string[]);
newPlatformChrome.getApplicationClasses$.mockReturnValue(applicationClasses$);
const chrome: any = {};
initChromeThemeApi(chrome);
return { chrome, brand$, applicationClasses$ };
}
afterEach(() => {
jest.resetAllMocks();
});
describe('setBrand', () => {
it('proxies to newPlatformChrome', () => {
const { chrome } = setup();
chrome.setBrand({
logo: 'foo.svg',
smallLogo: 'smallFoo.svg',
});
chrome.setBrand({
logo: 'baz',
});
expect(newPlatformChrome.setBrand.mock.calls).toMatchInlineSnapshot(`
Array [
Array [
Object {
"logo": "foo.svg",
"smallLogo": "smallFoo.svg",
},
],
Array [
Object {
"logo": "baz",
},
],
]
`);
});
});
describe('getBrand', () => {
it('returns named properties from cached values emitted from newPlatformChrome', () => {
const { chrome, brand$ } = setup();
expect(chrome.getBrand('logo')).toBe('foo');
expect(chrome.getBrand('smallLogo')).toBe('foo');
expect(chrome.getBrand()).toBe(undefined);
brand$.next({
logo: 'bar.svg',
smallLogo: 'smallBar.svg',
});
expect(chrome.getBrand('logo')).toBe('bar.svg');
expect(chrome.getBrand('smallLogo')).toBe('smallBar.svg');
expect(chrome.getBrand()).toBe(undefined);
});
});
describe('addApplicationClass', () => {
it('proxies each class as a separate argument to newPlatformChrome', () => {
const { chrome } = setup();
chrome.addApplicationClass('foo');
chrome.addApplicationClass(['bar', 'baz']);
chrome.addApplicationClass([]);
expect(newPlatformChrome.addApplicationClass.mock.calls).toMatchInlineSnapshot(`
Array [
Array [
"foo",
],
Array [
"bar",
],
Array [
"baz",
],
]
`);
});
});
describe('removeApplicationClass', () => {
it('proxies each class as a separate argument to newPlatformChrome', () => {
const { chrome } = setup();
chrome.removeApplicationClass('foo');
chrome.removeApplicationClass(['bar', 'baz']);
chrome.removeApplicationClass([]);
expect(newPlatformChrome.removeApplicationClass.mock.calls).toMatchInlineSnapshot(`
Array [
Array [
"foo",
],
Array [
"bar",
],
Array [
"baz",
],
]
`);
});
});
describe('getApplicationClasses', () => {
it('returns cached values emitted from newPlatformChrome as a single string', () => {
const { chrome, applicationClasses$ } = setup();
expect(chrome.getApplicationClasses()).toBe('');
applicationClasses$.next(['foo', 'bar']);
expect(chrome.getApplicationClasses()).toBe('foo bar');
applicationClasses$.next(['bar']);
expect(chrome.getApplicationClasses()).toBe('bar');
});
});

View file

@ -1,69 +0,0 @@
/*
* Licensed to Elasticsearch B.V. under one or more contributor
* license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright
* ownership. Elasticsearch B.V. licenses this file to you under
* the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
import * as Rx from 'rxjs';
import { npStart } from 'ui/new_platform';
import { ChromeBrand } from '../../../../../core/public';
const newPlatformChrome = npStart.core.chrome;
export function initChromeThemeApi(chrome: { [key: string]: any }) {
const brandCache$ = new Rx.BehaviorSubject<ChromeBrand>({});
newPlatformChrome.getBrand$().subscribe(brandCache$);
const applicationClassesCache$ = new Rx.BehaviorSubject<string[]>([]);
newPlatformChrome.getApplicationClasses$().subscribe(applicationClassesCache$);
chrome.setBrand = (brand: ChromeBrand) => {
newPlatformChrome.setBrand(brand);
return chrome;
};
chrome.getBrand = (key: keyof ChromeBrand) => {
return brandCache$.getValue()[key];
};
chrome.addApplicationClass = (classNames: string | string[] = []) => {
if (typeof classNames === 'string') {
classNames = [classNames];
}
for (const className of classNames) {
newPlatformChrome.addApplicationClass(className);
}
return chrome;
};
chrome.removeApplicationClass = (classNames: string | string[]) => {
if (typeof classNames === 'string') {
classNames = [classNames];
}
for (const className of classNames) {
newPlatformChrome.removeApplicationClass(className);
}
return chrome;
};
chrome.getApplicationClasses = () => {
return applicationClassesCache$.getValue().join(' ');
};
}

View file

@ -1,28 +0,0 @@
/*
* Licensed to Elasticsearch B.V. under one or more contributor
* license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright
* ownership. Elasticsearch B.V. licenses this file to you under
* the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
import { npSetup } from 'ui/new_platform';
const newPlatformUiSettingsClient = npSetup.core.uiSettings;
export function initUiSettingsApi(chrome) {
chrome.getUiSettingsClient = function () {
return newPlatformUiSettingsClient;
};
}

View file

@ -1,24 +0,0 @@
/*
* Licensed to Elasticsearch B.V. under one or more contributor
* license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright
* ownership. Elasticsearch B.V. licenses this file to you under
* the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
export function initChromeXsrfApi(chrome, internals) {
chrome.getXsrfToken = function () {
return internals.version;
};
}

View file

@ -1,118 +0,0 @@
/*
* Licensed to Elasticsearch B.V. under one or more contributor
* license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright
* ownership. Elasticsearch B.V. licenses this file to you under
* the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
import _ from 'lodash';
import angular from 'angular';
import { metadata } from '../metadata';
import '../state_management/global_state';
import '../config';
import '../notify';
import '../private';
import '../promises';
import '../directives/storage';
import '../directives/watch_multi';
import '../react_components';
import '../i18n';
import { initAngularApi } from './api/angular';
import appsApi from './api/apps';
import { initChromeControlsApi } from './api/controls';
import { initChromeNavApi } from './api/nav';
import { initChromeBadgeApi } from './api/badge';
import { initBreadcrumbsApi } from './api/breadcrumbs';
import templateApi from './api/template';
import { initChromeThemeApi } from './api/theme';
import { initChromeXsrfApi } from './api/xsrf';
import { initUiSettingsApi } from './api/ui_settings';
import { initLoadingCountApi } from './api/loading_count';
import { initSavedObjectClient } from './api/saved_object_client';
import { initChromeBasePathApi } from './api/base_path';
import { initChromeInjectedVarsApi } from './api/injected_vars';
import { initHelpExtensionApi } from './api/help_extension';
import { npStart } from '../new_platform';
export const chrome = {};
const internals = _.defaults(_.cloneDeep(metadata), {
basePath: '',
rootController: null,
rootTemplate: null,
showAppsLink: null,
xsrfToken: null,
devMode: true,
brand: null,
nav: [],
applicationClasses: [],
});
initUiSettingsApi(chrome);
initSavedObjectClient(chrome);
appsApi(chrome, internals);
initChromeXsrfApi(chrome, internals);
initChromeBasePathApi(chrome);
initChromeInjectedVarsApi(chrome);
initChromeNavApi(chrome, internals);
initChromeBadgeApi(chrome);
initBreadcrumbsApi(chrome, internals);
initLoadingCountApi(chrome, internals);
initHelpExtensionApi(chrome, internals);
initAngularApi(chrome, internals);
initChromeControlsApi(chrome);
templateApi(chrome, internals);
initChromeThemeApi(chrome);
npStart.core.chrome.setAppTitle(chrome.getAppTitle());
const waitForBootstrap = new Promise((resolve) => {
chrome.bootstrap = function (targetDomElement) {
// sets attribute on body for stylesheet sandboxing
document.body.setAttribute('id', `${internals.app.id}-app`);
chrome.setupAngular();
targetDomElement.setAttribute('kbn-chrome', 'true');
targetDomElement.setAttribute('ng-class', "{ 'hidden-chrome': !chrome.getVisible() }");
targetDomElement.className = 'app-wrapper';
angular.bootstrap(targetDomElement, ['kibana']);
resolve(targetDomElement);
};
});
/**
* ---- ATTENTION: Read documentation carefully before using this! ----
*
* Returns a promise, that resolves with an instance of the currently used Angular
* $injector service for usage outside of Angular.
* You can use this injector to get access to any other injectable component (service,
* constant, etc.) by using its get method.
*
* If you ever use Angular outside of an Angular context via this method, you should
* be really sure you know what you are doing!
*
* When using this method inside your code, you will need to stub it while running
* tests. Look into 'src/test_utils/public/stub_get_active_injector' for more information.
*/
chrome.dangerouslyGetActiveInjector = () => {
return waitForBootstrap.then((targetDomElement) => {
const $injector = angular.element(targetDomElement).injector();
if (!$injector) {
return Promise.reject('targetDomElement had no angular context after bootstrapping');
}
return $injector;
});
};

View file

@ -1,24 +0,0 @@
/*
* Licensed to Elasticsearch B.V. under one or more contributor
* license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright
* ownership. Elasticsearch B.V. licenses this file to you under
* the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
import { kbnChromeProvider } from './kbn_chrome';
export function directivesProvider(chrome, internals) {
kbnChromeProvider(chrome, internals);
}

View file

@ -1,9 +0,0 @@
<div class="app-wrapper-panel" data-test-subj="appA11yRoot">
<div id="globalBannerList"></div>
<div
class="application"
ng-class="'tab-' + getFirstPathSegment() + ' ' + chrome.getApplicationClasses()"
ng-view
></div>
</div>

View file

@ -1,106 +0,0 @@
/*
* Licensed to Elasticsearch B.V. under one or more contributor
* license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright
* ownership. Elasticsearch B.V. licenses this file to you under
* the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
import React from 'react';
import ReactDOM from 'react-dom';
import $ from 'jquery';
import { fatalError } from 'ui/notify/fatal_error';
import { uiModules } from '../../modules';
import template from './kbn_chrome.html';
import { I18nContext } from '../../i18n';
import { npStart } from '../../new_platform';
import {
chromeHeaderNavControlsRegistry,
NavControlSide,
} from '../../registry/chrome_header_nav_controls';
import { subscribeWithScope } from '../../../../../plugins/kibana_legacy/public';
export function kbnChromeProvider(chrome, internals) {
uiModules.get('kibana').directive('kbnChrome', () => {
return {
template() {
const $content = $(template);
const $app = $content.find('.application');
if (internals.rootController) {
$app.attr('ng-controller', internals.rootController);
}
if (internals.rootTemplate) {
$app.removeAttr('ng-view');
$app.html(internals.rootTemplate);
}
return $content;
},
controllerAs: 'chrome',
controller($scope, $location, Private) {
$scope.getFirstPathSegment = () => {
return $location.path().split('/')[1];
};
// Continue to support legacy nav controls not registered with the NP.
const navControls = Private(chromeHeaderNavControlsRegistry);
(navControls.bySide[NavControlSide.Left] || []).forEach((navControl) =>
npStart.core.chrome.navControls.registerLeft({
order: navControl.order,
mount: navControl.render,
})
);
(navControls.bySide[NavControlSide.Right] || []).forEach((navControl) =>
npStart.core.chrome.navControls.registerRight({
order: navControl.order,
mount: navControl.render,
})
);
// Non-scope based code (e.g., React)
// Banners
const bannerListContainer = document.getElementById('globalBannerList');
if (bannerListContainer) {
// This gets rendered manually by the legacy platform because this component must be inside the .app-wrapper
ReactDOM.render(
<I18nContext>{npStart.core.overlays.banners.getComponent()}</I18nContext>,
bannerListContainer
);
}
const chromeVisibility = subscribeWithScope(
$scope,
chrome.visible$,
{
next: () => {
// just makes sure change detection is triggered when chrome visibility changes
},
},
fatalError
);
$scope.$on('$destroy', () => {
chromeVisibility.unsubscribe();
});
return chrome;
},
};
});
}

View file

@ -1,64 +0,0 @@
/*
* Licensed to Elasticsearch B.V. under one or more contributor
* license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright
* ownership. Elasticsearch B.V. licenses this file to you under
* the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
import { SavedObjectsClientContract } from 'src/core/public';
import { ChromeBrand } from '../../../../core/public';
import { BadgeApi } from './api/badge';
import { BreadcrumbsApi } from './api/breadcrumbs';
import { HelpExtensionApi } from './api/help_extension';
import { ChromeNavLinks } from './api/nav';
export interface IInjector {
get<T>(injectable: string): T;
invoke<T, T2>(
injectable: (this: T2, ...args: any[]) => T,
self?: T2,
locals?: { [key: string]: any }
): T;
instantiate(constructor: Function, locals?: { [key: string]: any }): any;
}
declare interface Chrome extends ChromeNavLinks {
badge: BadgeApi;
breadcrumbs: BreadcrumbsApi;
helpExtension: HelpExtensionApi;
addBasePath<T = string>(path: T): T;
dangerouslyGetActiveInjector(): Promise<IInjector>;
getBasePath(): string;
getXsrfToken(): string;
getKibanaVersion(): string;
getSavedObjectsClient(): SavedObjectsClientContract;
getUiSettingsClient(): any;
setVisible(visible: boolean): any;
getInjected(key: string, defaultValue?: any): any;
setRootController(name: string, Controller: any): any;
setBrand(brand: ChromeBrand): this;
getBrand(key: keyof ChromeBrand): ChromeBrand[keyof ChromeBrand];
addApplicationClass(classNames: string | string[]): this;
removeApplicationClass(classNames: string | string[]): this;
getApplicationClasses(): string;
}
declare const chrome: Chrome;
// eslint-disable-next-line import/no-default-export
export default chrome;
export { Chrome };
export { Breadcrumb } from './api/breadcrumbs';
export { HelpExtension } from './api/help_extension';

View file

@ -1,23 +0,0 @@
/*
* Licensed to Elasticsearch B.V. under one or more contributor
* license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright
* ownership. Elasticsearch B.V. licenses this file to you under
* the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
import { chrome } from './chrome';
// eslint-disable-next-line import/no-default-export
export default chrome;

View file

@ -1,208 +0,0 @@
/*
* Licensed to Elasticsearch B.V. under one or more contributor
* license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright
* ownership. Elasticsearch B.V. licenses this file to you under
* the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
import expect from '@kbn/expect';
import sinon from 'sinon';
import ngMock from 'ng_mock';
import chrome from '../../chrome';
describe('Config service', () => {
let config;
let uiSettings;
let $q;
let $rootScope;
beforeEach(ngMock.module('kibana'));
beforeEach(
ngMock.inject(($injector) => {
config = $injector.get('config');
uiSettings = chrome.getUiSettingsClient();
$q = $injector.get('$q');
$rootScope = $injector.get('$rootScope');
})
);
describe('#getAll', () => {
it('calls uiSettings.getAll()', () => {
sinon.stub(uiSettings, 'getAll');
config.getAll();
sinon.assert.calledOnce(uiSettings.getAll);
sinon.assert.calledWithExactly(uiSettings.getAll);
uiSettings.getAll.restore();
});
});
describe('#get', () => {
it('calls uiSettings.get(key, default)', () => {
sinon.stub(uiSettings, 'get');
config.get('key', 'default');
sinon.assert.calledOnce(uiSettings.get);
sinon.assert.calledWithExactly(uiSettings.get, 'key', 'default');
uiSettings.get.restore();
});
});
describe('#isDeclared', () => {
it('calls uiSettings.isDeclared(key)', () => {
sinon.stub(uiSettings, 'isDeclared');
config.isDeclared('key');
sinon.assert.calledOnce(uiSettings.isDeclared);
sinon.assert.calledWithExactly(uiSettings.isDeclared, 'key');
uiSettings.isDeclared.restore();
});
});
describe('#isDefault', () => {
it('calls uiSettings.isDefault(key)', () => {
sinon.stub(uiSettings, 'isDefault');
config.isDefault('key');
sinon.assert.calledOnce(uiSettings.isDefault);
sinon.assert.calledWithExactly(uiSettings.isDefault, 'key');
uiSettings.isDefault.restore();
});
});
describe('#isCustom', () => {
it('calls uiSettings.isCustom(key)', () => {
sinon.stub(uiSettings, 'isCustom');
config.isCustom('key');
sinon.assert.calledOnce(uiSettings.isCustom);
sinon.assert.calledWithExactly(uiSettings.isCustom, 'key');
});
});
describe('#remove', () => {
it('calls uiSettings.remove(key)', () => {
sinon.stub(uiSettings, 'remove');
config.remove('foobar');
sinon.assert.calledOnce(uiSettings.remove);
sinon.assert.calledWithExactly(uiSettings.remove, 'foobar');
uiSettings.remove.restore();
});
it('returns an angular promise', () => {
const promise = config.remove('dateFormat:tz');
expect(promise).to.be.a($q);
});
});
describe('#set', () => {
it('returns an angular promise', () => {
const promise = config.set('dateFormat:tz', 'foo');
expect(promise).to.be.a($q);
});
it('strips $$-prefixed properties from plain objects', () => {
config.set('dateFormat:scaled', {
foo: 'bar',
$$bax: 'box',
});
expect(config.get('dateFormat:scaled')).to.eql({
foo: 'bar',
});
});
});
describe('$scope events', () => {
it('synchronously emits change:config on $rootScope when config changes', () => {
const stub = sinon.stub();
$rootScope.$on('change:config', stub);
config.set('foobar', 'baz');
sinon.assert.calledOnce(stub);
sinon.assert.calledWithExactly(stub, sinon.match({}), 'baz', undefined, 'foobar', config);
});
it('synchronously emits change:config.${key} on $rootScope when config changes', () => {
const stub = sinon.stub();
$rootScope.$on('change:config.foobar', stub);
config.set('foobar', 'baz');
sinon.assert.calledOnce(stub);
sinon.assert.calledWithExactly(stub, sinon.match({}), 'baz', undefined, 'foobar', config);
});
it('synchronously emits change:config on child scope when config changes', () => {
const stub = sinon.stub();
const $parent = $rootScope.$new(false);
const $scope = $rootScope.$new(false, $parent);
$scope.$on('change:config', stub);
config.set('foobar', 'baz');
sinon.assert.calledOnce(stub);
sinon.assert.calledWithExactly(stub, sinon.match({}), 'baz', undefined, 'foobar', config);
});
it('synchronously emits change:config.${key} on child scope when config changes', () => {
const stub = sinon.stub();
const $parent = $rootScope.$new(false);
const $scope = $rootScope.$new(false, $parent);
$scope.$on('change:config.foobar', stub);
config.set('foobar', 'baz');
sinon.assert.calledOnce(stub);
sinon.assert.calledWithExactly(stub, sinon.match({}), 'baz', undefined, 'foobar', config);
});
it('synchronously emits change:config on isolate scope when config changes', () => {
const stub = sinon.stub();
const $scope = $rootScope.$new(true);
$scope.$on('change:config', stub);
config.set('foobar', 'baz');
sinon.assert.calledOnce(stub);
sinon.assert.calledWithExactly(stub, sinon.match({}), 'baz', undefined, 'foobar', config);
});
it('synchronously emits change:config.${key} on isolate scope when config changes', () => {
const stub = sinon.stub();
const $scope = $rootScope.$new(true);
$scope.$on('change:config.foobar', stub);
config.set('foobar', 'baz');
sinon.assert.calledOnce(stub);
sinon.assert.calledWithExactly(stub, sinon.match({}), 'baz', undefined, 'foobar', config);
});
it('synchronously emits events when changes are inside a digest cycle', async () => {
const stub = sinon.stub();
$rootScope.$apply(() => {
$rootScope.$on('change:config.foobar', stub);
config.set('foobar', 'baz');
});
sinon.assert.calledOnce(stub);
sinon.assert.calledWithExactly(stub, sinon.match({}), 'baz', undefined, 'foobar', config);
});
it('synchronously emits events when changes are outside a digest cycle', async () => {
const stub = sinon.stub();
await new Promise((resolve) => {
setTimeout(function () {
const off = $rootScope.$on('change:config.foobar', stub);
config.set('foobar', 'baz');
// we unlisten to make sure that stub is not called before our assertions below
off();
resolve();
}, 0);
});
sinon.assert.calledOnce(stub);
sinon.assert.calledWithExactly(stub, sinon.match({}), 'baz', undefined, 'foobar', config);
});
});
});

View file

@ -1,110 +0,0 @@
/*
* Licensed to Elasticsearch B.V. under one or more contributor
* license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright
* ownership. Elasticsearch B.V. licenses this file to you under
* the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
import angular from 'angular';
import { fatalError } from 'ui/notify/fatal_error';
import chrome from '../chrome';
import { isPlainObject } from 'lodash';
import { uiModules } from '../modules';
import { subscribeWithScope } from '../../../../plugins/kibana_legacy/public';
const module = uiModules.get('kibana/config');
/**
* Angular tie-in to UiSettingsClient, which is implemented in vanilla JS. Designed
* to expose the exact same API as the config service that has existed since forever.
* @name config
*/
module.service(`config`, function ($rootScope, Promise) {
const uiSettings = chrome.getUiSettingsClient();
// direct bind sync methods
this.getAll = (...args) => uiSettings.getAll(...args);
this.get = (...args) => uiSettings.get(...args);
this.isDeclared = (...args) => uiSettings.isDeclared(...args);
this.isDefault = (...args) => uiSettings.isDefault(...args);
this.isCustom = (...args) => uiSettings.isCustom(...args);
this.isOverridden = (...args) => uiSettings.isOverridden(...args);
// modify remove() to use angular Promises
this.remove = (key) => Promise.resolve(uiSettings.remove(key));
// modify set() to use angular Promises and angular.toJson()
this.set = (key, value) =>
Promise.resolve(uiSettings.set(key, isPlainObject(value) ? angular.toJson(value) : value));
//////////////////////////////
//* angular specific methods *
//////////////////////////////
const subscription = subscribeWithScope(
$rootScope,
uiSettings.getUpdate$(),
{
next: ({ key, newValue, oldValue }) => {
$rootScope.$broadcast('change:config', newValue, oldValue, key, this);
$rootScope.$broadcast(`change:config.${key}`, newValue, oldValue, key, this);
},
},
fatalError
);
$rootScope.$on('$destroy', () => subscription.unsubscribe());
this.watchAll = function (handler, scope = $rootScope) {
// call handler immediately to initialize
handler(null, null, null, this);
return scope.$on('change:config', (event, ...args) => {
handler(...args);
});
};
this.watch = function (key, handler, scope = $rootScope) {
if (!this.isDeclared(key)) {
throw new Error(`Unexpected \`config.watch("${key}", fn)\` call on unrecognized configuration setting "${key}".
Setting an initial value via \`config.set("${key}", value)\` before binding
any custom setting configuration watchers for "${key}" may fix this issue.`);
}
// call handler immediately with current value
handler(this.get(key), null, key, uiSettings);
// call handler again on each change for this key
return scope.$on(`change:config.${key}`, (event, ...args) => {
handler(...args);
});
};
/**
* A little helper for binding config variables to $scopes
*
* @param {Scope} $scope - an angular $scope object
* @param {string} key - the config key to bind to
* @param {string} [property] - optional property name where the value should
* be stored. Defaults to the config key
* @return {function} - an unbind function
*/
this.bindToScope = function (scope, key, property = key) {
const onUpdate = (newVal) => {
scope[property] = newVal;
};
return this.watch(key, onUpdate, scope);
};
});

View file

@ -1,20 +0,0 @@
/*
* Licensed to Elasticsearch B.V. under one or more contributor
* license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright
* ownership. Elasticsearch B.V. licenses this file to you under
* the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
import './config';

View file

@ -1,89 +0,0 @@
/*
* Licensed to Elasticsearch B.V. under one or more contributor
* license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright
* ownership. Elasticsearch B.V. licenses this file to you under
* the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
import expect from '@kbn/expect';
import ngMock from 'ng_mock';
import $ from 'jquery';
import '../input_focus';
import uiRoutes from 'ui/routes';
describe('Input focus directive', function () {
let $compile;
let $rootScope;
let $timeout;
let element;
let $el;
let selectedEl;
let selectedText;
const inputValue = 'Input Text Value';
uiRoutes.enable();
beforeEach(ngMock.module('kibana'));
beforeEach(
ngMock.inject(function (_$compile_, _$rootScope_, _$timeout_) {
$compile = _$compile_;
$rootScope = _$rootScope_;
$timeout = _$timeout_;
$el = $('<div>');
$el.appendTo('body');
})
);
afterEach(function () {
$el.remove();
$el = null;
});
function renderEl(html) {
$rootScope.value = inputValue;
element = $compile(html)($rootScope);
element.appendTo($el);
$rootScope.$digest();
$timeout.flush();
selectedEl = document.activeElement;
if (selectedEl.value) {
selectedText = selectedEl.value.slice(selectedEl.selectionStart, selectedEl.selectionEnd);
}
}
it('should focus the input', function () {
renderEl('<input type="text" ng-model="value" input-focus />');
expect(selectedEl).to.equal(element[0]);
expect(selectedText.length).to.equal(0);
});
it('should select the text in the input', function () {
renderEl('<input type="text" ng-model="value" input-focus="select" />');
expect(selectedEl).to.equal(element[0]);
expect(selectedText.length).to.equal(inputValue.length);
expect(selectedText).to.equal(inputValue);
});
it('should not focus the input if disable-input-focus is set to true on the same element', function () {
renderEl('<input type="text" ng-model="value" input-focus disable-input-focus="true">');
expect(selectedEl).not.to.be(element[0]);
});
it('should still focus the input if disable-input-focus is falsy', function () {
renderEl('<input type="text" ng-model="value" input-focus disable-input-focus="false">');
expect(selectedEl).to.be(element[0]);
});
});

View file

@ -1,2 +0,0 @@
@import './input_datetime';
@import './saved_object_paginated_selectable'

View file

@ -1,5 +0,0 @@
.input-datetime-format {
font-size: $euiFontSizeXS;
color: $euiColorMediumShade;
padding: $euiSizeXS $euiSize;
}

View file

@ -1,70 +0,0 @@
saved-object-finder,
paginated-selectable-list {
.list-sort-button {
border-top-left-radius: 0 !important;
border-top-right-radius: 0 !important;
border: none;
padding: $euiSizeS $euiSize;
font-weight: $euiFontWeightRegular;
background-color: $euiColorLightestShade;
}
ul.li-striped {
li {
border: none;
}
li:nth-child(even) {
background-color: $euiColorLightestShade;
}
li:nth-child(odd) {
background-color: $euiColorEmptyShade;
}
.paginate-heading {
font-weight: $euiFontWeightRegular;
color: $euiColorDarkestShade;
}
.list-group-item {
padding: $euiSizeS $euiSize;
ul {
padding: 0;
display: flex;
flex-direction: row;
.finder-type {
margin-right: $euiSizeS;
}
}
a {
display: block;
color: $euiColorPrimary !important;
i {
color: shade($euiColorPrimary, 10%) !important;
margin-right: $euiSizeS;
}
}
&:first-child {
border-top-left-radius: 0 !important;
border-top-right-radius: 0 !important;
}
&.list-group-no-results p {
margin-bottom: 0 !important;
}
}
}
paginate {
paginate-controls {
margin: $euiSize;
}
}
}

View file

@ -1,87 +0,0 @@
/*
* Licensed to Elasticsearch B.V. under one or more contributor
* license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright
* ownership. Elasticsearch B.V. licenses this file to you under
* the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
import expect from '@kbn/expect';
import ngMock from 'ng_mock';
describe('$scope.$bind', function () {
let $rootScope;
let $scope;
beforeEach(ngMock.module('kibana'));
beforeEach(
ngMock.inject(function ($injector) {
$rootScope = $injector.get('$rootScope');
$scope = $rootScope.$new();
})
);
it('exposes $bind on all scopes', function () {
expect($rootScope.$bind).to.be.a('function');
expect($scope).to.have.property('$bind', $rootScope.$bind);
const $isoScope = $scope.$new(true);
expect($isoScope).to.have.property('$bind', $rootScope.$bind);
});
it("sets up binding from a parent scope to it's child", function () {
$rootScope.val = 'foo';
$scope.$bind('localVal', 'val');
expect($scope.localVal).to.be('foo');
$rootScope.val = 'bar';
expect($scope.localVal).to.be('foo'); // shouldn't have changed yet
$rootScope.$apply();
expect($scope.localVal).to.be('bar');
});
it('sets up a binding from the child to the parent scope', function () {
$rootScope.val = 'foo';
$scope.$bind('localVal', 'val');
expect($scope.localVal).to.be('foo');
$scope.localVal = 'bar';
expect($rootScope.val).to.be('foo'); // shouldn't have changed yet
$scope.$apply();
expect($rootScope.val).to.be('bar');
});
it('pulls from the scopes $parent by default', function () {
const $parent = $rootScope.$new();
const $self = $parent.$new();
$parent.val = 'foo';
$self.val = 'bar';
$self.$bind('localVal', 'val');
expect($self.localVal).to.be('foo');
});
it('accepts an alternate scope to read from', function () {
const $parent = $rootScope.$new();
const $self = $parent.$new();
$parent.val = 'foo';
$self.val = 'bar';
$self.$bind('localVal', 'val', $self);
expect($self.localVal).to.be('bar');
});
});

View file

@ -1,116 +0,0 @@
/*
* Licensed to Elasticsearch B.V. under one or more contributor
* license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright
* ownership. Elasticsearch B.V. licenses this file to you under
* the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
import angular from 'angular';
import { uiModules } from '../../modules';
uiModules.get('kibana').config(function ($provide) {
function strictEquality(a, b) {
// are the values equal? or, are they both NaN?
return a === b || (a !== a && b !== b);
}
function errorNotAssignable(source, target) {
throw Error(
'Unable to accept change to bound $scope property "' +
source +
'"' +
' because source expression "' +
target +
'" is not assignable!'
);
}
$provide.decorator('$rootScope', function ($delegate, $parse) {
/**
* Two-way bind a value from scope to another property on scope. This
* allow values on scope that work like they do in an isolate scope, but
* without requiring one.
*
* @param {expression} to - the location on scope to bind to
* @param {expression} from - the location on scope to bind from
* @param {Scope} $sourceScope - the scope to read "from" expression from
* @return {undefined}
*/
$delegate.constructor.prototype.$bind = function (to, from, $sourceScope) {
const $source = $sourceScope || this.$parent;
const $target = this;
// parse expressions
const $to = $parse(to);
if (!$to.assign) errorNotAssignable(to, from);
const $from = $parse(from);
// bind scopes to expressions
const getTarget = function () {
return $to($target);
};
const setTarget = function (v) {
return $to.assign($target, v);
};
const getSource = function () {
return $from($source);
};
const setSource = function (v) {
return $from.assignOrFail($source, v);
};
// to support writing from the child to the parent we need to know
// which source has changed. Track the source value and anytime it
// changes (even if the target value changed too) push from source
// to target. If the source hasn't changed then the change is from
// the target and push accordingly
let lastSourceVal = getSource();
$from.assignOrFail =
$from.assign ||
function () {
// revert the change and throw an error, child writes aren't supported
$to($target, (lastSourceVal = $from($source)));
errorNotAssignable(from, to);
};
// if we are syncing down a literal, then we use loose equality check
const strict = !$from.literal;
const compare = strict ? strictEquality : angular.equals;
// push the initial value down, start off in sync
setTarget(lastSourceVal);
$target.$watch(
function () {
const sourceVal = getSource();
const targetVal = getTarget();
const outOfSync = !compare(sourceVal, targetVal);
const sourceChanged = outOfSync && !compare(sourceVal, lastSourceVal);
if (sourceChanged) setTarget(sourceVal);
else if (outOfSync) setSource(targetVal);
return (lastSourceVal = sourceVal);
},
null,
!strict
);
};
return $delegate;
});
});

View file

@ -1,20 +0,0 @@
/*
* Licensed to Elasticsearch B.V. under one or more contributor
* license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright
* ownership. Elasticsearch B.V. licenses this file to you under
* the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
import './bind';

View file

@ -1,36 +0,0 @@
/*
* Licensed to Elasticsearch B.V. under one or more contributor
* license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright
* ownership. Elasticsearch B.V. licenses this file to you under
* the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
import { uiModules } from '../modules';
const module = uiModules.get('kibana');
module.directive('inputFocus', function ($parse, $timeout) {
return {
restrict: 'A',
link: function ($scope, $elem, attrs) {
const isDisabled = attrs.disableInputFocus && $parse(attrs.disableInputFocus)($scope);
if (!isDisabled) {
$timeout(function () {
$elem.focus();
if (attrs.inputFocus === 'select') $elem.select();
});
}
},
};
});

View file

@ -1,38 +0,0 @@
/*
* Licensed to Elasticsearch B.V. under one or more contributor
* license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright
* ownership. Elasticsearch B.V. licenses this file to you under
* the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
import { uiModules } from '../modules';
import { words, kebabCase } from 'lodash';
export function kbnUrlDirective(name) {
const attr = kebabCase(words(name).slice(1));
uiModules.get('kibana').directive(name, function (chrome) {
return {
restrict: 'A',
link: function ($scope, $el, $attr) {
$attr.$observe(name, function (val) {
$attr.$set(attr, chrome.addBasePath(val));
});
},
};
});
}
kbnUrlDirective('kbnHref');

View file

@ -1,96 +0,0 @@
/*
* Licensed to Elasticsearch B.V. under one or more contributor
* license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright
* ownership. Elasticsearch B.V. licenses this file to you under
* the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
import sinon from 'sinon';
import expect from '@kbn/expect';
import ngMock from 'ng_mock';
import '..';
import { EventsProvider } from '../../../events';
describe('listen component', function () {
let $rootScope;
let Events;
beforeEach(ngMock.module('kibana'));
beforeEach(
ngMock.inject(function ($injector, Private) {
$rootScope = $injector.get('$rootScope');
Events = Private(EventsProvider);
})
);
it('exposes the $listen method on all scopes', function () {
expect($rootScope.$listen).to.be.a('function');
expect($rootScope.$new().$listen).to.be.a('function');
});
it('binds to an event emitter', function () {
const emitter = new Events();
const $scope = $rootScope.$new();
function handler() {}
$scope.$listen(emitter, 'hello', handler);
expect(emitter._listeners.hello).to.have.length(1);
expect(emitter._listeners.hello[0].handler).to.be(handler);
});
it('binds to $scope, waiting for the destroy event', function () {
const emitter = new Events();
const $scope = $rootScope.$new();
sinon.stub($scope, '$on');
sinon.stub($rootScope, '$on');
function handler() {}
$scope.$listen(emitter, 'hello', handler);
expect($rootScope.$on).to.have.property('callCount', 0);
expect($scope.$on).to.have.property('callCount', 1);
const call = $scope.$on.firstCall;
expect(call.args[0]).to.be('$destroy');
expect(call.args[1]).to.be.a('function');
});
it('unbinds the event handler when $destroy is triggered', function () {
const emitter = new Events();
const $scope = $rootScope.$new();
sinon.stub($scope, '$on');
sinon.stub(emitter, 'off');
// set the listener
function handler() {}
$scope.$listen(emitter, 'hello', handler);
// get the unbinder that was registered to $scope
const unbinder = $scope.$on.firstCall.args[1];
// call the unbinder
expect(emitter.off).to.have.property('callCount', 0);
unbinder();
expect(emitter.off).to.have.property('callCount', 1);
// check that the off args were as expected
const call = emitter.off.firstCall;
expect(call.args[0]).to.be('hello');
expect(call.args[1]).to.be(handler);
});
});

View file

@ -1,20 +0,0 @@
/*
* Licensed to Elasticsearch B.V. under one or more contributor
* license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright
* ownership. Elasticsearch B.V. licenses this file to you under
* the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
import './listen';

View file

@ -1,23 +0,0 @@
/*
* Licensed to Elasticsearch B.V. under one or more contributor
* license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright
* ownership. Elasticsearch B.V. licenses this file to you under
* the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
import { uiModules } from '../../modules';
import { registerListenEventListener } from '../../../../../plugins/kibana_legacy/public';
uiModules.get('kibana').run(registerListenEventListener);

View file

@ -1,123 +0,0 @@
/*
* Licensed to Elasticsearch B.V. under one or more contributor
* license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright
* ownership. Elasticsearch B.V. licenses this file to you under
* the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
import angular from 'angular';
import sinon from 'sinon';
import expect from '@kbn/expect';
import ngMock from 'ng_mock';
import '..';
let init;
let $rootScope;
let $compile;
describe('render_directive', function () {
beforeEach(ngMock.module('kibana'));
beforeEach(
ngMock.inject(function ($injector) {
$rootScope = $injector.get('$rootScope');
$compile = $injector.get('$compile');
init = function init(markup = '', definition = {}) {
const $parentScope = $rootScope;
// create the markup
const $elem = angular.element('<render-directive>');
$elem.html(markup);
if (definition !== null) {
$parentScope.definition = definition;
$elem.attr('definition', 'definition');
}
// compile the directive
$compile($elem)($parentScope);
$parentScope.$apply();
const $directiveScope = $elem.isolateScope();
return { $parentScope, $directiveScope, $elem };
};
})
);
describe('directive requirements', function () {
it('should throw if not given a definition', function () {
expect(() => init('', null)).to.throwException(/must have a definition/);
});
});
describe('rendering with definition', function () {
it('should call link method', function () {
const markup = '<p>hello world</p>';
const definition = {
link: sinon.stub(),
};
init(markup, definition);
sinon.assert.callCount(definition.link, 1);
});
it('should call controller method', function () {
const markup = '<p>hello world</p>';
const definition = {
controller: sinon.stub(),
};
init(markup, definition);
sinon.assert.callCount(definition.controller, 1);
});
});
describe('definition scope binding', function () {
it('should accept two-way, attribute, and expression binding directives', function () {
const $el = angular.element(`
<render-directive
definition="definition"
two-way-prop="parentTwoWay"
attr="Simple Attribute"
expr="parentExpression()"
>
{{two}},{{attr}},{{expr()}}
</render-directive>
`);
const $parentScope = $rootScope.$new();
$parentScope.definition = {
scope: {
two: '=twoWayProp',
attr: '@',
expr: '&expr',
},
};
$parentScope.parentTwoWay = true;
$parentScope.parentExpression = function () {
return !$parentScope.parentTwoWay;
};
$compile($el)($parentScope);
$parentScope.$apply();
expect($el.text().trim()).to.eql('true,Simple Attribute,false');
$parentScope.parentTwoWay = false;
$parentScope.$apply();
expect($el.text().trim()).to.eql('false,Simple Attribute,true');
});
});
});

View file

@ -1,61 +0,0 @@
/*
* Licensed to Elasticsearch B.V. under one or more contributor
* license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright
* ownership. Elasticsearch B.V. licenses this file to you under
* the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
import { forOwn, noop } from 'lodash';
import '../../directives/bind';
const bindingRE = /^(=|=\?|&|@)([a-zA-Z0-9_$]+)?$/;
export function ApplyScopeBindingsProvider($parse) {
return function (bindings, $scope, $attrs) {
forOwn(bindings, (binding, local) => {
if (!bindingRE.test(binding)) {
throw new Error(`Invalid scope binding "${binding}". Expected it to match ${bindingRE}`);
}
const [, type, attribute = local] = binding.match(bindingRE);
const attr = $attrs[attribute];
switch (type) {
case '=':
$scope.$bind(local, attr);
break;
case '=?':
throw new Error(
'<render-directive> does not currently support optional two-way bindings.'
);
break;
case '&':
if (attr) {
const getter = $parse(attr);
$scope[local] = function () {
return getter($scope.$parent);
};
} else {
$scope[local] = noop;
}
break;
case '@':
$scope[local] = attr;
$attrs.$observe(attribute, (v) => ($scope[local] = v));
break;
}
});
};
}

View file

@ -1,20 +0,0 @@
/*
* Licensed to Elasticsearch B.V. under one or more contributor
* license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright
* ownership. Elasticsearch B.V. licenses this file to you under
* the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
import './render_directive';

View file

@ -1,90 +0,0 @@
/*
* Licensed to Elasticsearch B.V. under one or more contributor
* license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright
* ownership. Elasticsearch B.V. licenses this file to you under
* the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
import { isPlainObject } from 'lodash';
import { uiModules } from '../../modules';
import { ApplyScopeBindingsProvider } from './apply_scope_bindings';
/**
* The <render-directive> directive is useful for programmatically modifying or
* extending a view. It allows defining the majority of the directives behavior
* using a "definition" object, which the implementer can obtain from plugins (for instance).
*
* The definition object supports the parts of a directive definition that are
* easy enough to implement without having to hack angular, and does it's best to
* make sure standard directive life-cycle timing is respected.
*
* @param [Object] definition - the external configuration for this directive to assume
* @param [Function] definition.controller - a constructor used to create the controller for this directive
* @param [String] definition.controllerAs - a name where the controller should be stored on scope
* @param [Object] definition.scope - an object defining the binding properties for values read from
* attributes and bound to $scope. The keys of this object are the
* local names of $scope properties, and the values are a combination
* of the binding style (=, @, or &) and the external attribute name.
* See [the Angular docs]
* (https://code.angularjs.org/1.4.9/docs/api/ng/service/$compile#-scope-)
* for more info
* @param [Object|Function] definition.link - either a post link function or an object with pre and/or
* post link functions.
*/
uiModules.get('kibana').directive('renderDirective', function (Private) {
const applyScopeBindings = Private(ApplyScopeBindingsProvider);
return {
restrict: 'E',
scope: {
definition: '=',
},
template: function ($el) {
return $el.html();
},
controller: function ($scope, $element, $attrs, $transclude, $injector) {
if (!$scope.definition) throw new Error('render-directive must have a definition attribute');
const { controller, controllerAs, scope } = $scope.definition;
applyScopeBindings(scope, $scope, $attrs);
if (controller) {
if (controllerAs) {
$scope[controllerAs] = this;
}
const locals = { $scope, $element, $attrs, $transclude };
const controllerInstance = $injector.invoke(controller, this, locals) || this;
if (controllerAs) {
$scope[controllerAs] = controllerInstance;
}
}
},
link: {
pre($scope, $el, $attrs, controller) {
const { link } = $scope.definition;
const preLink = isPlainObject(link) ? link.pre : null;
if (preLink) preLink($scope, $el, $attrs, controller);
},
post($scope, $el, $attrs, controller) {
const { link } = $scope.definition;
const postLink = isPlainObject(link) ? link.post : link;
if (postLink) postLink($scope, $el, $attrs, controller);
},
},
};
});

View file

@ -1,32 +0,0 @@
/*
* Licensed to Elasticsearch B.V. under one or more contributor
* license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright
* ownership. Elasticsearch B.V. licenses this file to you under
* the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
import { uiModules } from '../../modules';
import { Storage } from '../../../../../plugins/kibana_utils/public';
const createService = function (type) {
return function ($window) {
return new Storage($window[type]);
};
};
uiModules
.get('kibana/storage')
.service('localStorage', createService('localStorage'))
.service('sessionStorage', createService('sessionStorage'));

View file

@ -1,216 +0,0 @@
/*
* Licensed to Elasticsearch B.V. under one or more contributor
* license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright
* ownership. Elasticsearch B.V. licenses this file to you under
* the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
import _ from 'lodash';
import ngMock from 'ng_mock';
import expect from '@kbn/expect';
import sinon from 'sinon';
describe('$scope.$watchMulti', function () {
let $rootScope;
let $scope;
beforeEach(ngMock.module('kibana'));
beforeEach(
ngMock.inject(function ($injector) {
$rootScope = $injector.get('$rootScope');
$scope = $rootScope.$new();
})
);
describe('basic functionality', function () {
it('exposes $watchMulti on all scopes', function () {
expect($rootScope.$watchMulti).to.be.a('function');
expect($scope).to.have.property('$watchMulti', $rootScope.$watchMulti);
const $isoScope = $scope.$new(true);
expect($isoScope).to.have.property('$watchMulti', $rootScope.$watchMulti);
});
it('returns a working unwatch function', function () {
$scope.a = 0;
$scope.b = 0;
let triggers = 0;
const unwatch = $scope.$watchMulti(['a', 'b'], function () {
triggers++;
});
// initial watch
$scope.$apply();
expect(triggers).to.be(1);
// prove that it triggers on change
$scope.a++;
$scope.$apply();
expect(triggers).to.be(2);
// remove watchers
expect($scope.$$watchers).to.not.have.length(0);
unwatch();
expect($scope.$$watchers).to.have.length(0);
// prove that it doesn't trigger anymore
$scope.a++;
$scope.$apply();
expect(triggers).to.be(2);
});
});
describe('simple scope watchers', function () {
it('only triggers a single watch on initialization', function () {
const stub = sinon.stub();
$scope.$watchMulti(['one', 'two', 'three'], stub);
$rootScope.$apply();
expect(stub.callCount).to.be(1);
});
it('only triggers a single watch when multiple values change', function () {
const stub = sinon.spy(function () {});
$scope.$watchMulti(['one', 'two', 'three'], stub);
$rootScope.$apply();
expect(stub.callCount).to.be(1);
$scope.one = 'a';
$scope.two = 'b';
$scope.three = 'c';
$rootScope.$apply();
expect(stub.callCount).to.be(2);
});
it('passes an array of the current and previous values, in order', function () {
const stub = sinon.spy(function () {});
$scope.one = 'a';
$scope.two = 'b';
$scope.three = 'c';
$scope.$watchMulti(['one', 'two', 'three'], stub);
$rootScope.$apply();
expect(stub.firstCall.args).to.eql([
['a', 'b', 'c'],
['a', 'b', 'c'],
]);
$scope.one = 'do';
$scope.two = 're';
$scope.three = 'mi';
$rootScope.$apply();
expect(stub.secondCall.args).to.eql([
['do', 're', 'mi'],
['a', 'b', 'c'],
]);
});
it('always has an up to date value', function () {
let count = 0;
$scope.vals = [1, 0];
$scope.$watchMulti(['vals[0]', 'vals[1]'], function (cur) {
expect(cur).to.eql($scope.vals);
count++;
});
const $child = $scope.$new();
$child.$watch('vals[0]', function (cur) {
$child.vals[1] = cur;
});
$rootScope.$apply();
expect(count).to.be(2);
});
});
describe('complex watch expressions', function () {
let stateWatchers;
let firstValue;
let secondValue;
beforeEach(function () {
const firstGetter = function () {
return firstValue;
};
const secondGetter = function () {
return secondValue;
};
stateWatchers = [
{
fn: $rootScope.$watch,
get: firstGetter,
},
{
fn: $rootScope.$watch,
get: secondGetter,
},
];
});
it('should trigger the watcher on initialization', function () {
const stub = sinon.stub();
firstValue = 'first';
secondValue = 'second';
$scope.$watchMulti(stateWatchers, stub);
$rootScope.$apply();
expect(stub.callCount).to.be(1);
expect(stub.firstCall.args[0]).to.eql([firstValue, secondValue]);
expect(stub.firstCall.args[1]).to.eql([firstValue, secondValue]);
});
});
describe('nested watchers', function () {
it('should trigger the handler at least once', function () {
const $scope = $rootScope.$new();
$scope.$$watchers = [
{
get: _.noop,
fn: _.noop,
eq: false,
last: false,
},
{
get: _.noop,
fn: registerWatchers,
eq: false,
last: false,
},
];
const first = sinon.stub();
const second = sinon.stub();
function registerWatchers() {
$scope.$watchMulti([first, second], function () {
expect(first.callCount).to.be.greaterThan(0);
expect(second.callCount).to.be.greaterThan(0);
});
}
$scope.$digest();
});
});
});

View file

@ -1,22 +0,0 @@
/*
* Licensed to Elasticsearch B.V. under one or more contributor
* license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright
* ownership. Elasticsearch B.V. licenses this file to you under
* the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
import { uiModules } from '../../modules';
import { watchMultiDecorator } from '../../../../../plugins/kibana_legacy/public';
uiModules.get('kibana').config(watchMultiDecorator);

Some files were not shown because too many files have changed in this diff Show more