Check for legacy imports in vis types and fix problems (#56763)

This commit is contained in:
Joe Reuter 2020-02-10 12:48:18 +01:00 committed by GitHub
parent 56571bac84
commit 09f1cad573
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
58 changed files with 901 additions and 765 deletions

View file

@ -11,7 +11,6 @@ bower_components
/src/plugins/data/common/es_query/kuery/ast/_generated_/**
/src/legacy/core_plugins/vis_type_timelion/public/_generated_/**
src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/lib/fixtures/mock_data
/src/legacy/ui/public/angular-bootstrap
/src/legacy/ui/public/flot-charts
/test/fixtures/scenarios
/src/legacy/core_plugins/console/public/webpackShims

View file

@ -302,6 +302,8 @@ module.exports = {
'test/plugin_functional/plugins/**/public/np_ready/**/*',
'test/plugin_functional/plugins/**/server/np_ready/**/*',
'src/legacy/core_plugins/**/public/np_ready/**/*',
'src/legacy/core_plugins/vis_type_*/public/**/*',
'!src/legacy/core_plugins/vis_type_*/public/legacy*',
'src/legacy/core_plugins/**/server/np_ready/**/*',
'x-pack/legacy/plugins/**/public/np_ready/**/*',
'x-pack/legacy/plugins/**/server/np_ready/**/*',

View file

@ -88,7 +88,6 @@ export const IGNORE_DIRECTORY_GLOBS = [
'src/babel-*',
'packages/*',
'packages/kbn-ui-framework/generator-kui',
'src/legacy/ui/public/angular-bootstrap',
'src/legacy/ui/public/flot-charts',
'src/legacy/ui/public/utils/lodash-mixins',
'test/functional/fixtures/es_archiver/visualize_source-filters',
@ -124,9 +123,6 @@ export const TEMPORARILY_IGNORED_PATHS = [
'src/legacy/core_plugins/timelion/server/series_functions/__tests__/fixtures/tlConfig.js',
'src/fixtures/config_upgrade_from_4.0.0_to_4.0.1-snapshot.json',
'src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/lib/fixtures/mock_data/terms/_seriesMultiple.js',
'src/legacy/ui/public/angular-bootstrap/bindHtml/bindHtml.js',
'src/legacy/ui/public/angular-bootstrap/tooltip/tooltip-html-unsafe-popup.html',
'src/legacy/ui/public/angular-bootstrap/tooltip/tooltip-popup.html',
'src/legacy/ui/public/assets/favicons/android-chrome-192x192.png',
'src/legacy/ui/public/assets/favicons/android-chrome-256x256.png',
'src/legacy/ui/public/assets/favicons/android-chrome-512x512.png',

View file

@ -21,7 +21,6 @@
// these are necessary to bootstrap the local angular.
// They can stay even after NP cutover
import angular from 'angular';
import 'ui/angular-bootstrap';
import { EuiIcon } from '@elastic/eui';
// @ts-ignore
import { StateProvider } from 'ui/state_management/state';
@ -64,6 +63,7 @@ import { createFieldChooserDirective } from './np_ready/components/field_chooser
import { createDiscoverFieldDirective } from './np_ready/components/field_chooser/discover_field';
import { CollapsibleSidebarProvider } from './np_ready/angular/directives/collapsible_sidebar/collapsible_sidebar';
import { DiscoverStartPlugins } from './plugin';
import { initAngularBootstrap } from '../../../../../plugins/kibana_legacy/public';
import { createCssTruncateDirective } from './np_ready/angular/directives/css_truncate';
// @ts-ignore
import { FixedScrollProvider } from './np_ready/angular/directives/fixed_scroll';
@ -85,6 +85,7 @@ import {
* needs to render, so in the end the current 'kibana' angular module is no longer necessary
*/
export function getInnerAngularModule(name: string, core: CoreStart, deps: DiscoverStartPlugins) {
initAngularBootstrap();
const module = initializeInnerAngularModule(name, core, deps.navigation, deps.data);
configureAppAngularModule(module, core as LegacyCoreStart, true);
return module;

View file

@ -19,6 +19,9 @@
import $ from 'jquery';
// TODO This is an integration test and thus requires a running platform. When moving to the new platform,
// this test has to be migrated to the newly created integration test environment.
// eslint-disable-next-line @kbn/eslint/no-restricted-paths
import { npStart } from 'ui/new_platform';
// @ts-ignore
import getStubIndexPattern from 'fixtures/stubbed_logstash_index_pattern';

View file

@ -21,7 +21,6 @@
// these are necessary to bootstrap the local angular.
// They can stay even after NP cutover
import angular from 'angular';
import 'ui/angular-bootstrap';
import 'angular-recursion';
import { i18nDirective, i18nFilter, I18nProvider } from '@kbn/i18n/angular';
import { CoreStart, LegacyCoreStart, IUiSettingsClient } from 'kibana/public';
@ -34,6 +33,9 @@ import {
StateManagementConfigProvider,
configureAppAngularModule,
} from './legacy_imports';
import { initAngularBootstrap } from '../../../../plugins/kibana_legacy/public';
initAngularBootstrap();
const thirdPartyAngularDependencies = ['ngSanitize', 'ui.bootstrap', 'RecursionHelper'];

View file

@ -22,11 +22,15 @@ import angular, { IRootScopeService, IScope, ICompileService } from 'angular';
import $ from 'jquery';
import 'angular-sanitize';
import 'angular-mocks';
import '../table_vis.mock';
import { getAngularModule } from '../get_inner_angular';
import { initTableVisLegacyModule } from '../table_vis_legacy_module';
import { npStart } from '../legacy_imports';
import { coreMock } from '../../../../../core/public/mocks';
jest.mock('ui/new_platform');
jest.mock('../../../../../plugins/kibana_legacy/public/angular/angular_config', () => ({
configureAppAngularModule: () => {},
}));
interface Sort {
columnIndex: number;
@ -69,7 +73,7 @@ describe('Table Vis - Paginated table', () => {
let paginatedTable: any;
const initLocalAngular = () => {
const tableVisModule = getAngularModule('kibana/table_vis', npStart.core);
const tableVisModule = getAngularModule('kibana/table_vis', coreMock.createStart());
initTableVisLegacyModule(tableVisModule);
};

View file

@ -1,46 +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 { createUiNewPlatformMock } from 'ui/new_platform/__mocks__/helpers';
import { StubBrowserStorage } from 'test_utils/stub_browser_storage';
import { injectedMetadataServiceMock } from '../../../../core/public/mocks';
jest.doMock('ui/new_platform', () => {
const npMock = createUiNewPlatformMock();
return {
npSetup: {
...npMock.npSetup,
core: {
...npMock.npSetup.core,
injectedMetadata: injectedMetadataServiceMock.createSetupContract(),
},
},
npStart: {
...npMock.npStart,
core: {
...npMock.npStart.core,
injectedMetadata: injectedMetadataServiceMock.createStartContract(),
},
},
};
});
Object.assign(window, {
sessionStorage: new StubBrowserStorage(),
});

View file

@ -21,21 +21,26 @@ import angular, { IRootScopeService, IScope, ICompileService } from 'angular';
import 'angular-mocks';
import 'angular-sanitize';
import $ from 'jquery';
import './table_vis.mock';
// @ts-ignore
import StubIndexPattern from 'test_utils/stub_index_pattern';
import { getAngularModule } from './get_inner_angular';
import { initTableVisLegacyModule } from './table_vis_legacy_module';
import { npStart, IAggConfig, tabifyAggResponse } from './legacy_imports';
import { tableVisTypeDefinition } from './table_vis_type';
import { Vis } from '../../visualizations/public';
import { setup as visualizationsSetup } from '../../visualizations/public/np_ready/public/legacy';
// eslint-disable-next-line
import { stubFields } from '../../../../plugins/data/public/stubs';
// eslint-disable-next-line
import { setFieldFormats } from '../../../../plugins/data/public/services';
import { tableVisResponseHandler } from './table_vis_response_handler';
import { coreMock } from '../../../../core/public/mocks';
// eslint-disable-next-line @kbn/eslint/no-restricted-paths
import { AggConfigs } from 'ui/agg_types';
import { tabifyAggResponse, IAggConfig } from './legacy_imports';
jest.mock('ui/new_platform');
jest.mock('../../../../plugins/kibana_legacy/public/angular/angular_config', () => ({
configureAppAngularModule: () => {},
}));
interface TableVisScope extends IScope {
[key: string]: any;
@ -79,14 +84,11 @@ describe('Table Vis - Controller', () => {
let stubIndexPattern: any;
const initLocalAngular = () => {
const tableVisModule = getAngularModule('kibana/table_vis', npStart.core);
const tableVisModule = getAngularModule('kibana/table_vis', coreMock.createStart());
initTableVisLegacyModule(tableVisModule);
};
beforeEach(initLocalAngular);
beforeAll(() => {
visualizationsSetup.types.createBaseVisualization(tableVisTypeDefinition);
});
beforeEach(angular.mock.module('kibana/table_vis'));
beforeEach(
@ -98,38 +100,38 @@ describe('Table Vis - Controller', () => {
);
beforeEach(() => {
setFieldFormats(({
getDefaultInstance: jest.fn(),
} as unknown) as any);
stubIndexPattern = new StubIndexPattern(
'logstash-*',
(cfg: any) => cfg,
'time',
stubFields,
npStart.core
coreMock.createStart()
);
});
function getRangeVis(params?: object) {
// @ts-ignore
return new Vis(stubIndexPattern, {
type: 'table',
params: params || {},
aggs: [
{ type: 'count', schema: 'metric' },
{
type: 'range',
schema: 'bucket',
params: {
field: 'bytes',
ranges: [
{ from: 0, to: 1000 },
{ from: 1000, to: 2000 },
],
return ({
type: tableVisTypeDefinition,
params: Object.assign({}, tableVisTypeDefinition.visConfig.defaults, params),
aggs: new AggConfigs(
stubIndexPattern,
[
{ type: 'count', schema: 'metric' },
{
type: 'range',
schema: 'bucket',
params: {
field: 'bytes',
ranges: [
{ from: 0, to: 1000 },
{ from: 1000, to: 2000 },
],
},
},
},
],
});
],
tableVisTypeDefinition.editorConfig.schemas.all
),
} as unknown) as Vis;
}
const dimensions = {
@ -241,13 +243,13 @@ describe('Table Vis - Controller', () => {
const vis = getRangeVis({ showPartialRows: true });
initController(vis);
expect(vis.isHierarchical()).toEqual(true);
expect(vis.type.hierarchicalData(vis)).toEqual(true);
});
test('passes partialRows:false to tabify based on the vis params', () => {
const vis = getRangeVis({ showPartialRows: false });
initController(vis);
expect(vis.isHierarchical()).toEqual(false);
expect(vis.type.hierarchicalData(vis)).toEqual(false);
});
});

View file

@ -21,10 +21,10 @@ import React from 'react';
import { EuiPanel } from '@elastic/eui';
import { i18n } from '@kbn/i18n';
import { ValidatedDualRange } from 'ui/validated_range';
import { VisOptionsProps } from '../../../vis_default_editor/public';
import { SelectOption, SwitchOption } from '../../../vis_type_vislib/public';
import { TagCloudVisParams } from '../types';
import { ValidatedDualRange } from '../legacy_imports';
function TagCloudOptions({ stateParams, setValue, vis }: VisOptionsProps<TagCloudVisParams>) {
const handleFontSizeChange = ([minFontSize, maxFontSize]: [string | number, string | number]) => {

View file

@ -21,9 +21,9 @@ import React from 'react';
import * as Rx from 'rxjs';
import { take } from 'rxjs/operators';
import { render, unmountComponentAtNode } from 'react-dom';
import { I18nProvider } from '@kbn/i18n/react';
import { getFormat } from 'ui/visualize/loader/pipeline_helpers/utilities';
import { I18nContext } from 'ui/i18n';
import { getFormat } from '../legacy_imports';
import { Label } from './label';
import { TagCloud } from './tag_cloud';
@ -65,9 +65,9 @@ export function createTagCloudVisualization({ colors }) {
this._containerNode.appendChild(this._feedbackNode);
this._feedbackMessage = React.createRef();
render(
<I18nContext>
<I18nProvider>
<FeedbackMessage ref={this._feedbackMessage} />
</I18nContext>,
</I18nProvider>,
this._feedbackNode
);

View file

@ -18,3 +18,5 @@
*/
export { Schemas } from 'ui/agg_types';
export { ValidatedDualRange } from 'ui/validated_range';
export { getFormat } from 'ui/visualize/loader/pipeline_helpers/utilities';

View file

@ -17,8 +17,8 @@
* under the License.
*/
import chrome from 'ui/chrome';
import { getUISettings } from '../../services';
export function getDefaultQueryLanguage() {
return chrome.getUiSettingsClient().get('search:queryLanguage');
return getUISettings().get('search:queryLanguage');
}

View file

@ -19,11 +19,11 @@
import handlebars from 'handlebars/dist/handlebars';
import { isNumber } from 'lodash';
import { npStart } from 'ui/new_platform';
import { inputFormats, outputFormats, isDuration } from '../lib/durations';
import { getFieldFormats } from '../../services';
export const createTickFormatter = (format = '0,0.[00]', template, getConfig = null) => {
const fieldFormats = npStart.plugins.data.fieldFormats;
const fieldFormats = getFieldFormats();
if (!template) template = '{{value}}';
const render = handlebars.compile(template, { knownHelpersOnly: true });

View file

@ -17,9 +17,9 @@
* under the License.
*/
import { npStart } from 'ui/new_platform';
import { createTickFormatter } from './tick_formatter';
import { getFieldFormatsRegistry } from '../../../../../../test_utils/public/stub_field_formats';
import { setFieldFormats } from '../../services';
const mockUiSettings = {
get: item => {
@ -46,9 +46,7 @@ const mockCore = {
};
describe('createTickFormatter(format, template)', () => {
npStart.plugins.data = {
fieldFormats: getFieldFormatsRegistry(mockCore),
};
setFieldFormats(getFieldFormatsRegistry(mockCore));
test('returns a number with two decimal place by default', () => {
const fn = createTickFormatter();

View file

@ -26,6 +26,10 @@ jest.mock('plugins/data', () => {
};
});
jest.mock('../lib/get_default_query_language', () => ({
getDefaultQueryLanguage: () => 'kuery',
}));
import { GaugePanelConfig } from './gauge';
describe('GaugePanelConfig', () => {

View file

@ -30,13 +30,11 @@ import { createBrushHandler } from '../lib/create_brush_handler';
import { fetchFields } from '../lib/fetch_fields';
import { extractIndexPatterns } from '../../../../../plugins/vis_type_timeseries/common/extract_index_patterns';
import { esKuery } from '../../../../../plugins/data/public';
import { npStart } from 'ui/new_platform';
import { getSavedObjectsClient, getUISettings, getDataStart, getCoreStart } from '../services';
import { CoreStartContextProvider } from '../contexts/query_input_bar_context';
import { KibanaContextProvider } from '../../../../../plugins/kibana_react/public';
import { Storage } from '../../../../../plugins/kibana_utils/public';
import { timefilter } from 'ui/timefilter';
const VIS_STATE_DEBOUNCE_DELAY = 200;
const APP_NAME = 'VisEditor';
@ -52,7 +50,7 @@ export class VisEditor extends Component {
visFields: props.visFields,
extractedIndexPatterns: [''],
};
this.onBrush = createBrushHandler(timefilter);
this.onBrush = createBrushHandler(getDataStart().query.timefilter);
this.visDataSubject = new Rx.BehaviorSubject(this.props.visData);
this.visData$ = this.visDataSubject.asObservable().pipe(share());
@ -60,8 +58,8 @@ export class VisEditor extends Component {
// core dependencies required by React components downstream.
this.coreContext = {
appName: APP_NAME,
uiSettings: npStart.core.uiSettings,
savedObjectsClient: npStart.core.savedObjects.client,
uiSettings: getUISettings(),
savedObjectsClient: getSavedObjectsClient(),
store: this.localStorage,
};
}
@ -175,8 +173,8 @@ export class VisEditor extends Component {
services={{
appName: APP_NAME,
storage: this.localStorage,
data: npStart.plugins.data,
...npStart.core,
data: getDataStart(),
...getCoreStart(),
}}
>
<div className="tvbEditor" data-test-subj="tvbVisEditor">

View file

@ -20,7 +20,6 @@
import _, { isArray, last, get } from 'lodash';
import React, { Component } from 'react';
import PropTypes from 'prop-types';
import { npStart } from 'ui/new_platform';
import { createTickFormatter } from '../../lib/tick_formatter';
import { calculateLabel } from '../../../../../../../plugins/vis_type_timeseries/common/calculate_label';
import { isSortable } from './is_sortable';
@ -28,6 +27,7 @@ import { EuiToolTip, EuiIcon } from '@elastic/eui';
import { replaceVars } from '../../lib/replace_vars';
import { fieldFormats } from '../../../../../../../plugins/data/public';
import { FormattedMessage } from '@kbn/i18n/react';
import { getFieldFormats } from '../../../services';
import { METRIC_TYPES } from '../../../../../../../plugins/vis_type_timeseries/common/metric_types';
@ -49,7 +49,7 @@ export class TableVis extends Component {
constructor(props) {
super(props);
const fieldFormatsService = npStart.plugins.data.fieldFormats;
const fieldFormatsService = getFieldFormats();
const DateFormat = fieldFormatsService.getType(fieldFormats.FIELD_FORMAT_IDS.DATE);
this.dateFormatter = new DateFormat({}, this.props.getConfig);

View file

@ -22,7 +22,6 @@ import React, { Component } from 'react';
import reactCSS from 'reactcss';
import { startsWith, get, cloneDeep, map } from 'lodash';
import { toastNotifications } from 'ui/notify';
import { htmlIdGenerator } from '@elastic/eui';
import { ScaleType } from '@elastic/charts';
@ -36,6 +35,7 @@ import { areFieldsDifferent } from '../../lib/charts';
import { createXaxisFormatter } from '../../lib/create_xaxis_formatter';
import { isBackgroundDark } from '../../../lib/set_is_reversed';
import { STACKED_OPTIONS } from '../../../visualizations/constants';
import { getCoreStart } from '../../../services';
export class TimeseriesVisualization extends Component {
static propTypes = {
@ -108,6 +108,7 @@ export class TimeseriesVisualization extends Component {
createTickFormatter(get(model, 'formatter'), get(model, 'value_template'), getConfig);
componentDidUpdate() {
const toastNotifications = getCoreStart().notifications.toasts;
if (
this.showToastNotification &&
this.notificationReason !== this.showToastNotification.reason

View file

@ -32,4 +32,4 @@ const plugins: Readonly<MetricsPluginSetupDependencies> = {
const pluginInstance = plugin({} as PluginInitializerContext);
export const setup = pluginInstance.setup(npSetup.core, plugins);
export const start = pluginInstance.start(npStart.core);
export const start = pluginInstance.start(npStart.core, npStart.plugins);

View file

@ -17,4 +17,8 @@
* under the License.
*/
export * from './vega_config_provider';
export { PersistedState } from 'ui/persisted_state';
// @ts-ignore
export { defaultFeedbackMessage } from 'ui/vis/default_feedback_message';
// @ts-ignore
export { timezoneProvider } from 'ui/vis/lib/timezone';

View file

@ -16,19 +16,16 @@
* specific language governing permissions and limitations
* under the License.
*/
import { kfetch } from 'ui/kfetch';
import { toastNotifications } from 'ui/notify';
import { i18n } from '@kbn/i18n';
import { extractIndexPatterns } from '../../../../../plugins/vis_type_timeseries/common/extract_index_patterns';
import { getCoreStart } from '../services';
export async function fetchFields(indexPatterns = ['*']) {
const patterns = Array.isArray(indexPatterns) ? indexPatterns : [indexPatterns];
try {
const indexFields = await Promise.all(
patterns.map(pattern => {
return kfetch({
method: 'GET',
pathname: '/api/metrics/fields',
return getCoreStart().http.get('/api/metrics/fields', {
query: {
index: pattern,
},
@ -43,7 +40,7 @@ export async function fetchFields(indexPatterns = ['*']) {
}, {});
return fields;
} catch (error) {
toastNotifications.addDanger({
getCoreStart().notifications.toasts.addDanger({
title: i18n.translate('visTypeTimeseries.fetchFields.loadIndexPatternFieldsErrorMessage', {
defaultMessage: 'Unable to load index_pattern fields',
}),

View file

@ -19,11 +19,11 @@
import { get } from 'lodash';
import { i18n } from '@kbn/i18n';
import { PersistedState } from 'ui/persisted_state';
import { ExpressionFunction, KibanaContext, Render } from '../../../../plugins/expressions/public';
// @ts-ignore
import { metricsRequestHandler } from './request_handler';
import { PersistedState } from './legacy_imports';
const name = 'tsvb';
type Context = KibanaContext | null;

View file

@ -18,8 +18,7 @@
*/
import { i18n } from '@kbn/i18n';
// @ts-ignore
import { defaultFeedbackMessage } from 'ui/vis/default_feedback_message';
import { defaultFeedbackMessage } from './legacy_imports';
// @ts-ignore
import { metricsRequestHandler } from './request_handler';

View file

@ -16,29 +16,31 @@
* specific language governing permissions and limitations
* under the License.
*/
import {
PluginInitializerContext,
CoreSetup,
CoreStart,
Plugin,
SavedObjectsClientContract,
IUiSettingsClient,
} from '../../../../core/public';
import { PluginInitializerContext, CoreSetup, CoreStart, Plugin } from 'kibana/public';
import { Plugin as ExpressionsPublicPlugin } from '../../../../plugins/expressions/public';
import { VisualizationsSetup } from '../../visualizations/public';
import { createMetricsFn } from './metrics_fn';
import { metricsVisDefinition } from './metrics_type';
import { setSavedObjectsClient, setUISettings, setI18n } from './services';
import {
setSavedObjectsClient,
setUISettings,
setI18n,
setFieldFormats,
setCoreStart,
setDataStart,
} from './services';
import { DataPublicPluginStart } from '../../../../plugins/data/public';
/** @internal */
export interface MetricsPluginSetupDependencies {
expressions: ReturnType<ExpressionsPublicPlugin['setup']>;
visualizations: VisualizationsSetup;
}
export interface MetricsVisualizationDependencies {
uiSettings: IUiSettingsClient;
savedObjectsClient: SavedObjectsClientContract;
/** @internal */
export interface MetricsPluginStartDependencies {
data: DataPublicPluginStart;
}
/** @internal */
@ -58,9 +60,11 @@ export class MetricsPlugin implements Plugin<Promise<void>, void> {
visualizations.types.createReactVisualization(metricsVisDefinition);
}
public start(core: CoreStart) {
// nothing to do here yet
public start(core: CoreStart, { data }: MetricsPluginStartDependencies) {
setSavedObjectsClient(core.savedObjects);
setI18n(core.i18n);
setFieldFormats(data.fieldFormats);
setDataStart(data);
setCoreStart(core);
}
}

View file

@ -18,10 +18,8 @@
*/
import { validateInterval } from './lib/validate_interval';
import { timezoneProvider } from 'ui/vis/lib/timezone';
import { timefilter } from 'ui/timefilter';
import { kfetch } from 'ui/kfetch';
import { getUISettings } from './services';
import { timezoneProvider } from './legacy_imports';
import { getUISettings, getDataStart, getCoreStart } from './services';
export const metricsRequestHandler = async ({
uiState,
@ -34,7 +32,7 @@ export const metricsRequestHandler = async ({
const config = getUISettings();
const timezone = timezoneProvider(config)();
const uiStateObj = uiState.get(visParams.type, {});
const parsedTimeRange = timefilter.calculateBounds(timeRange);
const parsedTimeRange = getDataStart().query.timefilter.timefilter.calculateBounds(timeRange);
const scaledDataFormat = config.get('dateFormat:scaled');
const dateFormat = config.get('dateFormat');
@ -44,9 +42,7 @@ export const metricsRequestHandler = async ({
validateInterval(parsedTimeRange, visParams, maxBuckets);
const resp = await kfetch({
pathname: '/api/metrics/vis/data',
method: 'POST',
const resp = await getCoreStart().http.post('/api/metrics/vis/data', {
body: JSON.stringify({
timerange: {
timezone,

View file

@ -17,13 +17,22 @@
* under the License.
*/
import { I18nStart, SavedObjectsStart, IUiSettingsClient } from 'src/core/public';
import { I18nStart, SavedObjectsStart, IUiSettingsClient, CoreStart } from 'src/core/public';
import { createGetterSetter } from '../../../../plugins/kibana_utils/public';
import { DataPublicPluginStart, FieldFormatsStart } from '../../../../plugins/data/public';
export const [getUISettings, setUISettings] = createGetterSetter<IUiSettingsClient>('UISettings');
export const [getFieldFormats, setFieldFormats] = createGetterSetter<FieldFormatsStart>(
'FieldFormats'
);
export const [getSavedObjectsClient, setSavedObjectsClient] = createGetterSetter<SavedObjectsStart>(
'SavedObjectsClient'
);
export const [getCoreStart, setCoreStart] = createGetterSetter<CoreStart>('CoreStart');
export const [getDataStart, setDataStart] = createGetterSetter<DataPublicPluginStart>('DataStart');
export const [getI18n, setI18n] = createGetterSetter<I18nStart>('I18n');

View file

@ -33,9 +33,9 @@ import {
} from '@elastic/charts';
import { EuiIcon } from '@elastic/eui';
import { timezoneProvider } from 'ui/vis/lib/timezone';
import { timezoneProvider } from '../../../legacy_imports';
import { eventBus, ACTIVE_CURSOR } from '../../lib/active_cursor';
import chrome from 'ui/chrome';
import { getUISettings } from '../../../services';
import { GRID_LINE_CONFIG, ICON_TYPES_MAP, STACKED_OPTIONS } from '../../constants';
import { AreaSeriesDecorator } from './decorators/area_decorator';
import { BarSeriesDecorator } from './decorators/bar_decorator';
@ -85,7 +85,7 @@ export const TimeSeries = ({
}, []); // eslint-disable-line
const tooltipFormatter = decorateFormatter(xAxisFormatter);
const uiSettings = chrome.getUiSettingsClient();
const uiSettings = getUISettings();
const timeZone = timezoneProvider(uiSettings)();
const hasBarChart = series.some(({ bars }) => bars.show);

View file

@ -0,0 +1,55 @@
/*
* 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 { createGetterSetter } from '../../../../../plugins/kibana_utils/common';
import { DataPublicPluginStart } from '../../../../../plugins/data/public';
import { IUiSettingsClient, NotificationsStart, SavedObjectsStart } from 'kibana/public';
import { dataPluginMock } from '../../../../../plugins/data/public/mocks';
import { coreMock } from '../../../../../core/public/mocks';
export const [getData, setData] = createGetterSetter<DataPublicPluginStart>('Data');
setData(dataPluginMock.createStartContract());
export const [getNotifications, setNotifications] = createGetterSetter<NotificationsStart>(
'Notifications'
);
setNotifications(coreMock.createStart().notifications);
export const [getUISettings, setUISettings] = createGetterSetter<IUiSettingsClient>('UISettings');
setUISettings(coreMock.createStart().uiSettings);
export const [getSavedObjects, setSavedObjects] = createGetterSetter<SavedObjectsStart>(
'SavedObjects'
);
setSavedObjects(coreMock.createStart().savedObjects);
export const [getInjectedVars, setInjectedVars] = createGetterSetter<{
esShardTimeout: number;
enableExternalUrls: boolean;
emsTileLayerId: unknown;
}>('InjectedVars');
setInjectedVars({
emsTileLayerId: {},
enableExternalUrls: true,
esShardTimeout: 10000,
});
export const getEsShardTimeout = () => getInjectedVars().esShardTimeout;
export const getEnableExternalUrls = () => getInjectedVars().enableExternalUrls;
export const getEmsTileLayerId = () => getInjectedVars().emsTileLayerId;

View file

@ -43,6 +43,9 @@ import { SearchCache } from '../data_model/search_cache';
import { setup as visualizationsSetup } from '../../../visualizations/public/np_ready/public/legacy';
import { createVegaTypeDefinition } from '../vega_type';
// TODO This is an integration test and thus requires a running platform. When moving to the new platform,
// this test has to be migrated to the newly created integration test environment.
// eslint-disable-next-line @kbn/eslint/no-restricted-paths
import { npStart } from 'ui/new_platform';
const THRESHOLD = 0.1;

View file

@ -24,7 +24,7 @@ import compactStringify from 'json-stringify-pretty-compact';
import hjson from 'hjson';
import { i18n } from '@kbn/i18n';
import { toastNotifications } from 'ui/notify';
import { getNotifications } from '../services';
import { VisParams } from '../vega_fn';
import { VegaHelpMenu } from './vega_help_menu';
import { VegaActionsMenu } from './vega_actions_menu';
@ -50,7 +50,7 @@ function format(value: string, stringify: typeof compactStringify, options?: any
return stringify(spec, options);
} catch (err) {
// This is a common case - user tries to format an invalid HJSON text
toastNotifications.addError(err, {
getNotifications().toasts.addError(err, {
title: i18n.translate('visTypeVega.editor.formatError', {
defaultMessage: 'Error formatting spec',
}),

View file

@ -21,7 +21,7 @@ import _ from 'lodash';
import moment from 'moment';
import { i18n } from '@kbn/i18n';
import { getEsShardTimeout } from '../helpers';
import { getEsShardTimeout } from '../services';
const TIMEFILTER = '%timefilter%';
const AUTOINTERVAL = '%autointerval%';

View file

@ -21,10 +21,6 @@ import { cloneDeep } from 'lodash';
import moment from 'moment';
import { EsQueryParser } from './es_query_parser';
jest.mock('../helpers', () => ({
getEsShardTimeout: jest.fn(() => '10000'),
}));
const second = 1000;
const minute = 60 * second;
const hour = 60 * minute;
@ -47,6 +43,8 @@ function create(min, max, dashboardCtx) {
return inst;
}
jest.mock('../services');
describe(`EsQueryParser time`, () => {
test(`roundInterval(4s)`, () => {
expect(EsQueryParser._roundInterval(4 * second)).toBe(`1s`);

View file

@ -18,6 +18,7 @@
*/
import { SearchCache } from './search_cache';
jest.mock('../services');
describe(`SearchCache`, () => {
class FauxEs {

View file

@ -18,6 +18,7 @@
*/
import { TimeCache } from './time_cache';
jest.mock('../services');
describe(`TimeCache`, () => {
class FauxTimefilter {

View file

@ -20,6 +20,7 @@
import { cloneDeep } from 'lodash';
import { VegaParser } from './vega_parser';
import { bypassExternalUrlCheck } from '../vega_view/vega_base_view';
jest.mock('../services');
describe(`VegaParser._setDefaultValue`, () => {
function check(spec, expected, ...params) {

View file

@ -17,7 +17,9 @@
* under the License.
*/
import chrome from 'ui/chrome';
export const getEsShardTimeout = () => chrome.getInjected('esShardTimeout');
export const getEnableExternalUrls = () => chrome.getInjected('enableExternalUrls');
// @ts-ignore
export { defaultFeedbackMessage } from 'ui/vis/default_feedback_message';
// @ts-ignore
export { KibanaMapLayer } from 'ui/vis/map/kibana_map_layer';
// @ts-ignore
export { KibanaMap } from 'ui/vis/map/kibana_map';

View file

@ -21,7 +21,13 @@ import { LegacyDependenciesPlugin, LegacyDependenciesPluginSetup } from './shim'
import { Plugin as ExpressionsPublicPlugin } from '../../../../plugins/expressions/public';
import { Plugin as DataPublicPlugin } from '../../../../plugins/data/public';
import { VisualizationsSetup } from '../../visualizations/public';
import { setNotifications, setData, setSavedObjects } from './services';
import {
setNotifications,
setData,
setSavedObjects,
setInjectedVars,
setUISettings,
} from './services';
import { createVegaFn } from './vega_fn';
import { createVegaTypeDefinition } from './vega_type';
@ -59,6 +65,13 @@ export class VegaPlugin implements Plugin<Promise<void>, void> {
core: CoreSetup,
{ data, expressions, visualizations, __LEGACY }: VegaPluginSetupDependencies
) {
setInjectedVars({
esShardTimeout: core.injectedMetadata.getInjectedVar('esShardTimeout') as number,
enableExternalUrls: core.injectedMetadata.getInjectedVar('enableExternalUrls') as boolean,
emsTileLayerId: core.injectedMetadata.getInjectedVar('emsTileLayerId', true),
});
setUISettings(core.uiSettings);
const visualizationDependencies: Readonly<VegaVisualizationDependencies> = {
core,
plugins: {

View file

@ -21,6 +21,7 @@ import { SavedObjectsStart } from 'kibana/public';
import { NotificationsStart } from 'src/core/public';
import { DataPublicPluginStart } from '../../../../plugins/data/public';
import { createGetterSetter } from '../../../../plugins/kibana_utils/public';
import { IUiSettingsClient } from '../../../../core/public';
export const [getData, setData] = createGetterSetter<DataPublicPluginStart>('Data');
@ -28,6 +29,18 @@ export const [getNotifications, setNotifications] = createGetterSetter<Notificat
'Notifications'
);
export const [getUISettings, setUISettings] = createGetterSetter<IUiSettingsClient>('UISettings');
export const [getSavedObjects, setSavedObjects] = createGetterSetter<SavedObjectsStart>(
'SavedObjects'
);
export const [getInjectedVars, setInjectedVars] = createGetterSetter<{
esShardTimeout: number;
enableExternalUrls: boolean;
emsTileLayerId: unknown;
}>('InjectedVars');
export const getEsShardTimeout = () => getInjectedVars().esShardTimeout;
export const getEnableExternalUrls = () => getInjectedVars().enableExternalUrls;
export const getEmsTileLayerId = () => getInjectedVars().emsTileLayerId;

View file

@ -17,7 +17,10 @@
* under the License.
*/
// TODO remove this file as soon as serviceSettings is exposed in the new platform
// eslint-disable-next-line @kbn/eslint/no-restricted-paths
import chrome from 'ui/chrome';
// eslint-disable-next-line @kbn/eslint/no-restricted-paths
import 'ui/vis/map/service_settings';
import { CoreStart, Plugin } from 'kibana/public';

View file

@ -19,7 +19,7 @@
import { i18n } from '@kbn/i18n';
// @ts-ignore
import { defaultFeedbackMessage } from 'ui/vis/default_feedback_message';
import { defaultFeedbackMessage } from './legacy_imports';
import { Status } from '../../visualizations/public';
import { DefaultEditorSize } from '../../vis_default_editor/public';
import { VegaVisualizationDependencies } from './plugin';

View file

@ -17,7 +17,6 @@
* under the License.
*/
import chrome from 'ui/chrome';
import $ from 'jquery';
import moment from 'moment';
import dateMath from '@elastic/datemath';
@ -29,7 +28,7 @@ import { i18n } from '@kbn/i18n';
import { TooltipHandler } from './vega_tooltip';
import { esFilters } from '../../../../../plugins/data/public';
import { getEnableExternalUrls } from '../helpers/vega_config_provider';
import { getEnableExternalUrls } from '../services';
vega.scheme('elastic', VISUALIZATION_COLORS);
@ -279,20 +278,14 @@ export class VegaBaseView {
* @param {string} [index] as defined in Kibana, or default if missing
*/
async removeFilterHandler(query, index) {
const $injector = await chrome.dangerouslyGetActiveInjector();
const indexId = await this._findIndex(index);
const filter = esFilters.buildQueryFilter(query, indexId);
// This is a workaround for the https://github.com/elastic/kibana/issues/18863
// Once fixed, replace with a direct call (no await is needed because its not async)
// this._queryfilter.removeFilter(filter);
$injector.get('$rootScope').$evalAsync(() => {
try {
this._filterManager.removeFilter(filter);
} catch (err) {
this.onError(err);
}
});
try {
this._filterManager.removeFilter(filter);
} catch (err) {
this.onError(err);
}
}
removeAllFiltersHandler() {

View file

@ -19,7 +19,7 @@
import L from 'leaflet';
import 'leaflet-vega';
import { KibanaMapLayer } from 'ui/vis/map/kibana_map_layer';
import { KibanaMapLayer } from '../legacy_imports';
export class VegaMapLayer extends KibanaMapLayer {
constructor(spec, options) {

View file

@ -17,12 +17,12 @@
* under the License.
*/
import { KibanaMap } from 'ui/vis/map/kibana_map';
import * as vega from 'vega-lib';
import { i18n } from '@kbn/i18n';
import { VegaBaseView } from './vega_base_view';
import { VegaMapLayer } from './vega_map_layer';
import { i18n } from '@kbn/i18n';
import chrome from 'ui/chrome';
import { KibanaMap } from '../legacy_imports';
import { getEmsTileLayerId, getUISettings } from '../services';
export class VegaMapView extends VegaBaseView {
async _initViewCustomizations() {
@ -35,10 +35,10 @@ export class VegaMapView extends VegaBaseView {
const tmsServices = await this._serviceSettings.getTMSServices();
// In some cases, Vega may be initialized twice, e.g. after awaiting...
if (!this._$container) return;
const emsTileLayerId = chrome.getInjected('emsTileLayerId', true);
const emsTileLayerId = getEmsTileLayerId();
const mapStyle =
mapConfig.mapStyle === 'default' ? emsTileLayerId.bright : mapConfig.mapStyle;
const isDarkMode = chrome.getUiSettingsClient().get('theme:darkMode');
const isDarkMode = getUISettings().get('theme:darkMode');
baseMapOpts = tmsServices.find(s => s.id === mapStyle);
baseMapOpts = {
...baseMapOpts,

View file

@ -21,6 +21,7 @@ import sinon from 'sinon';
import ngMock from 'ng_mock';
import expect from '@kbn/expect';
// eslint-disable-next-line @kbn/eslint/no-restricted-paths
import { aggResponseIndex } from 'ui/agg_response';
import { vislibSeriesResponseHandler } from '../response_handler';

View file

@ -1,10 +0,0 @@
angular.module('ui.bootstrap.bindHtml', [])
.directive('bindHtmlUnsafe', function () {
return function (scope, element, attr) {
element.addClass('ng-binding').data('$binding', attr.bindHtmlUnsafe);
scope.$watch(attr.bindHtmlUnsafe, function bindHtmlUnsafeWatchAction(value) {
element.html(value || '');
});
};
});

View file

@ -1,47 +0,0 @@
/* eslint-disable */
/**
* TODO: Write custom components that address our needs to directly and deprecate these Bootstrap components.
*/
import 'angular';
import { uiModules } from 'ui/modules';
uiModules.get('kibana', [
'ui.bootstrap',
]);
/*
* angular-ui-bootstrap
* http://angular-ui.github.io/bootstrap/
* Version: 0.12.1 - 2015-02-20
* License: MIT
*/
angular.module('ui.bootstrap', [
'ui.bootstrap.tpls',
'ui.bootstrap.bindHtml',
'ui.bootstrap.tooltip',
]);
angular.module('ui.bootstrap.tpls', [
'template/tooltip/tooltip-html-unsafe-popup.html',
'template/tooltip/tooltip-popup.html',
]);
import './bindHtml/bindHtml';
import './tooltip/tooltip';
import tooltipUnsafePopup from './tooltip/tooltip-html-unsafe-popup.html';
angular.module('template/tooltip/tooltip-html-unsafe-popup.html', []).run(['$templateCache', function($templateCache) {
$templateCache.put('template/tooltip/tooltip-html-unsafe-popup.html', tooltipUnsafePopup);
}]);
import tooltipPopup from './tooltip/tooltip-popup.html';
angular.module('template/tooltip/tooltip-popup.html', []).run(['$templateCache', function($templateCache) {
$templateCache.put('template/tooltip/tooltip-popup.html', tooltipPopup);
}]);

View file

@ -1,152 +0,0 @@
angular.module('ui.bootstrap.position', [])
/**
* A set of utility methods that can be use to retrieve position of DOM elements.
* It is meant to be used where we need to absolute-position DOM elements in
* relation to other, existing elements (this is the case for tooltips, popovers,
* typeahead suggestions etc.).
*/
.factory('$position', ['$document', '$window', function ($document, $window) {
function getStyle(el, cssprop) {
if (el.currentStyle) { //IE
return el.currentStyle[cssprop];
} else if ($window.getComputedStyle) {
return $window.getComputedStyle(el)[cssprop];
}
// finally try and get inline style
return el.style[cssprop];
}
/**
* Checks if a given element is statically positioned
* @param element - raw DOM element
*/
function isStaticPositioned(element) {
return (getStyle(element, 'position') || 'static' ) === 'static';
}
/**
* returns the closest, non-statically positioned parentOffset of a given element
* @param element
*/
var parentOffsetEl = function (element) {
var docDomEl = $document[0];
var offsetParent = element.offsetParent || docDomEl;
while (offsetParent && offsetParent !== docDomEl && isStaticPositioned(offsetParent) ) {
offsetParent = offsetParent.offsetParent;
}
return offsetParent || docDomEl;
};
return {
/**
* Provides read-only equivalent of jQuery's position function:
* http://api.jquery.com/position/
*/
position: function (element) {
var elBCR = this.offset(element);
var offsetParentBCR = { top: 0, left: 0 };
var offsetParentEl = parentOffsetEl(element[0]);
if (offsetParentEl != $document[0]) {
offsetParentBCR = this.offset(angular.element(offsetParentEl));
offsetParentBCR.top += offsetParentEl.clientTop - offsetParentEl.scrollTop;
offsetParentBCR.left += offsetParentEl.clientLeft - offsetParentEl.scrollLeft;
}
var boundingClientRect = element[0].getBoundingClientRect();
return {
width: boundingClientRect.width || element.prop('offsetWidth'),
height: boundingClientRect.height || element.prop('offsetHeight'),
top: elBCR.top - offsetParentBCR.top,
left: elBCR.left - offsetParentBCR.left
};
},
/**
* Provides read-only equivalent of jQuery's offset function:
* http://api.jquery.com/offset/
*/
offset: function (element) {
var boundingClientRect = element[0].getBoundingClientRect();
return {
width: boundingClientRect.width || element.prop('offsetWidth'),
height: boundingClientRect.height || element.prop('offsetHeight'),
top: boundingClientRect.top + ($window.pageYOffset || $document[0].documentElement.scrollTop),
left: boundingClientRect.left + ($window.pageXOffset || $document[0].documentElement.scrollLeft)
};
},
/**
* Provides coordinates for the targetEl in relation to hostEl
*/
positionElements: function (hostEl, targetEl, positionStr, appendToBody) {
var positionStrParts = positionStr.split('-');
var pos0 = positionStrParts[0], pos1 = positionStrParts[1] || 'center';
var hostElPos,
targetElWidth,
targetElHeight,
targetElPos;
hostElPos = appendToBody ? this.offset(hostEl) : this.position(hostEl);
targetElWidth = targetEl.prop('offsetWidth');
targetElHeight = targetEl.prop('offsetHeight');
var shiftWidth = {
center: function () {
return hostElPos.left + hostElPos.width / 2 - targetElWidth / 2;
},
left: function () {
return hostElPos.left;
},
right: function () {
return hostElPos.left + hostElPos.width;
}
};
var shiftHeight = {
center: function () {
return hostElPos.top + hostElPos.height / 2 - targetElHeight / 2;
},
top: function () {
return hostElPos.top;
},
bottom: function () {
return hostElPos.top + hostElPos.height;
}
};
switch (pos0) {
case 'right':
targetElPos = {
top: shiftHeight[pos1](),
left: shiftWidth[pos0]()
};
break;
case 'left':
targetElPos = {
top: shiftHeight[pos1](),
left: hostElPos.left - targetElWidth
};
break;
case 'bottom':
targetElPos = {
top: shiftHeight[pos0](),
left: shiftWidth[pos1]()
};
break;
default:
targetElPos = {
top: hostElPos.top - targetElHeight,
left: shiftWidth[pos1]()
};
break;
}
return targetElPos;
}
};
}]);

View file

@ -1,374 +0,0 @@
import './position';
/**
* The following features are still outstanding: animation as a
* function, placement as a function, inside, support for more triggers than
* just mouse enter/leave, html tooltips, and selector delegation.
*/
angular.module( 'ui.bootstrap.tooltip', [ 'ui.bootstrap.position' ] )
/**
* The $tooltip service creates tooltip- and popover-like directives as well as
* houses global options for them.
*/
.provider( '$tooltip', function () {
// The default options tooltip and popover.
var defaultOptions = {
placement: 'top',
animation: true,
popupDelay: 0
};
// Default hide triggers for each show trigger
var triggerMap = {
'mouseenter': 'mouseleave',
'click': 'click',
'focus': 'blur'
};
// The options specified to the provider globally.
var globalOptions = {};
/**
* `options({})` allows global configuration of all tooltips in the
* application.
*
* var app = angular.module( 'App', ['ui.bootstrap.tooltip'], function( $tooltipProvider ) {
* // place tooltips left instead of top by default
* $tooltipProvider.options( { placement: 'left' } );
* });
*/
this.options = function( value ) {
angular.extend( globalOptions, value );
};
/**
* This allows you to extend the set of trigger mappings available. E.g.:
*
* $tooltipProvider.setTriggers( 'openTrigger': 'closeTrigger' );
*/
this.setTriggers = function setTriggers ( triggers ) {
angular.extend( triggerMap, triggers );
};
/**
* This is a helper function for translating camel-case to snake-case.
*/
function snake_case(name){
var regexp = /[A-Z]/g;
var separator = '-';
return name.replace(regexp, function(letter, pos) {
return (pos ? separator : '') + letter.toLowerCase();
});
}
/**
* Returns the actual instance of the $tooltip service.
* TODO support multiple triggers
*/
this.$get = [ '$window', '$compile', '$timeout', '$document', '$position', '$interpolate', function ( $window, $compile, $timeout, $document, $position, $interpolate ) {
return function $tooltip ( type, prefix, defaultTriggerShow ) {
var options = angular.extend( {}, defaultOptions, globalOptions );
/**
* Returns an object of show and hide triggers.
*
* If a trigger is supplied,
* it is used to show the tooltip; otherwise, it will use the `trigger`
* option passed to the `$tooltipProvider.options` method; else it will
* default to the trigger supplied to this directive factory.
*
* The hide trigger is based on the show trigger. If the `trigger` option
* was passed to the `$tooltipProvider.options` method, it will use the
* mapped trigger from `triggerMap` or the passed trigger if the map is
* undefined; otherwise, it uses the `triggerMap` value of the show
* trigger; else it will just use the show trigger.
*/
function getTriggers ( trigger ) {
var show = trigger || options.trigger || defaultTriggerShow;
var hide = triggerMap[show] || show;
return {
show: show,
hide: hide
};
}
var directiveName = snake_case( type );
var startSym = $interpolate.startSymbol();
var endSym = $interpolate.endSymbol();
var template =
'<div '+ directiveName +'-popup '+
'title="'+startSym+'title'+endSym+'" '+
'content="'+startSym+'content'+endSym+'" '+
'placement="'+startSym+'placement'+endSym+'" '+
'animation="animation" '+
'is-open="isOpen"'+
'>'+
'</div>';
return {
restrict: 'EA',
compile: function (tElem, tAttrs) {
var tooltipLinker = $compile( template );
return function link ( scope, element, attrs ) {
var tooltip;
var tooltipLinkedScope;
var transitionTimeout;
var popupTimeout;
var appendToBody = angular.isDefined( options.appendToBody ) ? options.appendToBody : false;
var triggers = getTriggers( undefined );
var hasEnableExp = angular.isDefined(attrs[prefix+'Enable']);
var ttScope = scope.$new(true);
var positionTooltip = function () {
var ttPosition = $position.positionElements(element, tooltip, ttScope.placement, appendToBody);
ttPosition.top += 'px';
ttPosition.left += 'px';
// Now set the calculated positioning.
tooltip.css( ttPosition );
};
// By default, the tooltip is not open.
// TODO add ability to start tooltip opened
ttScope.isOpen = false;
function toggleTooltipBind () {
if ( ! ttScope.isOpen ) {
showTooltipBind();
} else {
hideTooltipBind();
}
}
// Show the tooltip with delay if specified, otherwise show it immediately
function showTooltipBind() {
if(hasEnableExp && !scope.$eval(attrs[prefix+'Enable'])) {
return;
}
prepareTooltip();
if ( ttScope.popupDelay ) {
// Do nothing if the tooltip was already scheduled to pop-up.
// This happens if show is triggered multiple times before any hide is triggered.
if (!popupTimeout) {
popupTimeout = $timeout( show, ttScope.popupDelay, false );
popupTimeout
.then(reposition => reposition())
.catch((error) => {
// if the timeout is canceled then the string `canceled` is thrown. To prevent
// this from triggering an 'unhandled promise rejection' in angular 1.5+ the
// $timeout service explicitly tells $q that the promise it generated is "handled"
// but that does not include down chain promises like the one created by calling
// `popupTimeout.then()`. Because of this we need to ignore the "canceled" string
// and only propagate real errors
if (error !== 'canceled') {
throw error
}
});
}
} else {
show()();
}
}
function hideTooltipBind () {
scope.$evalAsync(function () {
hide();
});
}
// Show the tooltip popup element.
function show() {
popupTimeout = null;
// If there is a pending remove transition, we must cancel it, lest the
// tooltip be mysteriously removed.
if ( transitionTimeout ) {
$timeout.cancel( transitionTimeout );
transitionTimeout = null;
}
// Don't show empty tooltips.
if ( ! ttScope.content ) {
return angular.noop;
}
createTooltip();
// Set the initial positioning.
tooltip.css({ top: 0, left: 0, display: 'block' });
ttScope.$digest();
positionTooltip();
// And show the tooltip.
ttScope.isOpen = true;
ttScope.$digest(); // digest required as $apply is not called
// Return positioning function as promise callback for correct
// positioning after draw.
return positionTooltip;
}
// Hide the tooltip popup element.
function hide() {
// First things first: we don't show it anymore.
ttScope.isOpen = false;
//if tooltip is going to be shown after delay, we must cancel this
$timeout.cancel( popupTimeout );
popupTimeout = null;
// And now we remove it from the DOM. However, if we have animation, we
// need to wait for it to expire beforehand.
// FIXME: this is a placeholder for a port of the transitions library.
if ( ttScope.animation ) {
if (!transitionTimeout) {
transitionTimeout = $timeout(removeTooltip, 500);
}
} else {
removeTooltip();
}
}
function createTooltip() {
// There can only be one tooltip element per directive shown at once.
if (tooltip) {
removeTooltip();
}
tooltipLinkedScope = ttScope.$new();
tooltip = tooltipLinker(tooltipLinkedScope, function (tooltip) {
if ( appendToBody ) {
$document.find( 'body' ).append( tooltip );
} else {
element.after( tooltip );
}
});
}
function removeTooltip() {
transitionTimeout = null;
if (tooltip) {
tooltip.remove();
tooltip = null;
}
if (tooltipLinkedScope) {
tooltipLinkedScope.$destroy();
tooltipLinkedScope = null;
}
}
function prepareTooltip() {
prepPlacement();
prepPopupDelay();
}
/**
* Observe the relevant attributes.
*/
attrs.$observe( type, function ( val ) {
ttScope.content = val;
if (!val && ttScope.isOpen ) {
hide();
}
});
attrs.$observe( prefix+'Title', function ( val ) {
ttScope.title = val;
});
function prepPlacement() {
var val = attrs[ prefix + 'Placement' ];
ttScope.placement = angular.isDefined( val ) ? val : options.placement;
}
function prepPopupDelay() {
var val = attrs[ prefix + 'PopupDelay' ];
var delay = parseInt( val, 10 );
ttScope.popupDelay = ! isNaN(delay) ? delay : options.popupDelay;
}
var unregisterTriggers = function () {
element.unbind(triggers.show, showTooltipBind);
element.unbind(triggers.hide, hideTooltipBind);
};
function prepTriggers() {
var val = attrs[ prefix + 'Trigger' ];
unregisterTriggers();
triggers = getTriggers( val );
if ( triggers.show === triggers.hide ) {
element.bind( triggers.show, toggleTooltipBind );
} else {
element.bind( triggers.show, showTooltipBind );
element.bind( triggers.hide, hideTooltipBind );
}
}
prepTriggers();
var animation = scope.$eval(attrs[prefix + 'Animation']);
ttScope.animation = angular.isDefined(animation) ? !!animation : options.animation;
var appendToBodyVal = scope.$eval(attrs[prefix + 'AppendToBody']);
appendToBody = angular.isDefined(appendToBodyVal) ? appendToBodyVal : appendToBody;
// if a tooltip is attached to <body> we need to remove it on
// location change as its parent scope will probably not be destroyed
// by the change.
if ( appendToBody ) {
scope.$on('$locationChangeSuccess', function closeTooltipOnLocationChangeSuccess () {
if ( ttScope.isOpen ) {
hide();
}
});
}
// Make sure tooltip is destroyed and removed.
scope.$on('$destroy', function onDestroyTooltip() {
$timeout.cancel( transitionTimeout );
$timeout.cancel( popupTimeout );
unregisterTriggers();
removeTooltip();
ttScope = null;
});
};
}
};
};
}];
})
.directive( 'tooltip', [ '$tooltip', function ( $tooltip ) {
return $tooltip( 'tooltip', 'tooltip', 'mouseenter' );
}])
.directive( 'tooltipPopup', function () {
return {
restrict: 'EA',
replace: true,
scope: { content: '@', placement: '@', animation: '&', isOpen: '&' },
templateUrl: 'template/tooltip/tooltip-popup.html'
};
})
.directive( 'tooltipHtmlUnsafe', [ '$tooltip', function ( $tooltip ) {
return $tooltip( 'tooltipHtmlUnsafe', 'tooltip', 'mouseenter' );
}])
.directive( 'tooltipHtmlUnsafePopup', function () {
return {
restrict: 'EA',
replace: true,
scope: { content: '@', placement: '@', animation: '&', isOpen: '&' },
templateUrl: 'template/tooltip/tooltip-html-unsafe-popup.html'
};
});

View file

@ -0,0 +1,17 @@
/* eslint-disable */
import angular from 'angular';
export function initBindHtml() {
angular
.module('ui.bootstrap.bindHtml', [])
.directive('bindHtmlUnsafe', function() {
return function(scope, element, attr) {
element.addClass('ng-binding').data('$binding', attr.bindHtmlUnsafe);
scope.$watch(attr.bindHtmlUnsafe, function bindHtmlUnsafeWatchAction(value) {
element.html(value || '');
});
};
});
}

View file

@ -0,0 +1,50 @@
/* eslint-disable */
import { once } from 'lodash';
import angular from 'angular';
// @ts-ignore
import { initBindHtml } from './bind_html/bind_html';
// @ts-ignore
import { initBootstrapTooltip } from './tooltip/tooltip';
import tooltipPopup from './tooltip/tooltip_popup.html';
import tooltipUnsafePopup from './tooltip/tooltip_html_unsafe_popup.html';
export const initAngularBootstrap = once(() => {
/*
* angular-ui-bootstrap
* http://angular-ui.github.io/bootstrap/
* Version: 0.12.1 - 2015-02-20
* License: MIT
*/
angular.module('ui.bootstrap', [
'ui.bootstrap.tpls',
'ui.bootstrap.bindHtml',
'ui.bootstrap.tooltip',
]);
angular.module('ui.bootstrap.tpls', [
'template/tooltip/tooltip-html-unsafe-popup.html',
'template/tooltip/tooltip-popup.html',
]);
initBindHtml();
initBootstrapTooltip();
angular.module('template/tooltip/tooltip-html-unsafe-popup.html', []).run([
'$templateCache',
function($templateCache: any) {
$templateCache.put('template/tooltip/tooltip-html-unsafe-popup.html', tooltipUnsafePopup);
},
]);
angular.module('template/tooltip/tooltip-popup.html', []).run([
'$templateCache',
function($templateCache: any) {
$templateCache.put('template/tooltip/tooltip-popup.html', tooltipPopup);
},
]);
});

View file

@ -0,0 +1,167 @@
/* eslint-disable */
import angular from 'angular';
export function initBootstrapPosition() {
angular
.module('ui.bootstrap.position', [])
/**
* A set of utility methods that can be use to retrieve position of DOM elements.
* It is meant to be used where we need to absolute-position DOM elements in
* relation to other, existing elements (this is the case for tooltips, popovers,
* typeahead suggestions etc.).
*/
.factory('$position', [
'$document',
'$window',
function($document, $window) {
function getStyle(el, cssprop) {
if (el.currentStyle) {
//IE
return el.currentStyle[cssprop];
} else if ($window.getComputedStyle) {
return $window.getComputedStyle(el)[cssprop];
}
// finally try and get inline style
return el.style[cssprop];
}
/**
* Checks if a given element is statically positioned
* @param element - raw DOM element
*/
function isStaticPositioned(element) {
return (getStyle(element, 'position') || 'static') === 'static';
}
/**
* returns the closest, non-statically positioned parentOffset of a given element
* @param element
*/
const parentOffsetEl = function(element) {
const docDomEl = $document[0];
let offsetParent = element.offsetParent || docDomEl;
while (offsetParent && offsetParent !== docDomEl && isStaticPositioned(offsetParent)) {
offsetParent = offsetParent.offsetParent;
}
return offsetParent || docDomEl;
};
return {
/**
* Provides read-only equivalent of jQuery's position function:
* http://api.jquery.com/position/
*/
position: function(element) {
const elBCR = this.offset(element);
let offsetParentBCR = { top: 0, left: 0 };
const offsetParentEl = parentOffsetEl(element[0]);
if (offsetParentEl != $document[0]) {
offsetParentBCR = this.offset(angular.element(offsetParentEl));
offsetParentBCR.top += offsetParentEl.clientTop - offsetParentEl.scrollTop;
offsetParentBCR.left += offsetParentEl.clientLeft - offsetParentEl.scrollLeft;
}
const boundingClientRect = element[0].getBoundingClientRect();
return {
width: boundingClientRect.width || element.prop('offsetWidth'),
height: boundingClientRect.height || element.prop('offsetHeight'),
top: elBCR.top - offsetParentBCR.top,
left: elBCR.left - offsetParentBCR.left,
};
},
/**
* Provides read-only equivalent of jQuery's offset function:
* http://api.jquery.com/offset/
*/
offset: function(element) {
const boundingClientRect = element[0].getBoundingClientRect();
return {
width: boundingClientRect.width || element.prop('offsetWidth'),
height: boundingClientRect.height || element.prop('offsetHeight'),
top:
boundingClientRect.top +
($window.pageYOffset || $document[0].documentElement.scrollTop),
left:
boundingClientRect.left +
($window.pageXOffset || $document[0].documentElement.scrollLeft),
};
},
/**
* Provides coordinates for the targetEl in relation to hostEl
*/
positionElements: function(hostEl, targetEl, positionStr, appendToBody) {
const positionStrParts = positionStr.split('-');
const pos0 = positionStrParts[0];
const pos1 = positionStrParts[1] || 'center';
let hostElPos;
let targetElWidth;
let targetElHeight;
let targetElPos;
hostElPos = appendToBody ? this.offset(hostEl) : this.position(hostEl);
targetElWidth = targetEl.prop('offsetWidth');
targetElHeight = targetEl.prop('offsetHeight');
const shiftWidth = {
center: function() {
return hostElPos.left + hostElPos.width / 2 - targetElWidth / 2;
},
left: function() {
return hostElPos.left;
},
right: function() {
return hostElPos.left + hostElPos.width;
},
};
const shiftHeight = {
center: function() {
return hostElPos.top + hostElPos.height / 2 - targetElHeight / 2;
},
top: function() {
return hostElPos.top;
},
bottom: function() {
return hostElPos.top + hostElPos.height;
},
};
switch (pos0) {
case 'right':
targetElPos = {
top: shiftHeight[pos1](),
left: shiftWidth[pos0](),
};
break;
case 'left':
targetElPos = {
top: shiftHeight[pos1](),
left: hostElPos.left - targetElWidth,
};
break;
case 'bottom':
targetElPos = {
top: shiftHeight[pos0](),
left: shiftWidth[pos1](),
};
break;
default:
targetElPos = {
top: hostElPos.top - targetElHeight,
left: shiftWidth[pos1](),
};
break;
}
return targetElPos;
},
};
},
]);
}

View file

@ -0,0 +1,423 @@
/* eslint-disable */
import angular from 'angular';
import { initBootstrapPosition } from './position';
export function initBootstrapTooltip() {
initBootstrapPosition();
/**
* The following features are still outstanding: animation as a
* function, placement as a function, inside, support for more triggers than
* just mouse enter/leave, html tooltips, and selector delegation.
*/
angular
.module('ui.bootstrap.tooltip', ['ui.bootstrap.position'])
/**
* The $tooltip service creates tooltip- and popover-like directives as well as
* houses global options for them.
*/
.provider('$tooltip', function() {
// The default options tooltip and popover.
const defaultOptions = {
placement: 'top',
animation: true,
popupDelay: 0,
};
// Default hide triggers for each show trigger
const triggerMap = {
mouseenter: 'mouseleave',
click: 'click',
focus: 'blur',
};
// The options specified to the provider globally.
const globalOptions = {};
/**
* `options({})` allows global configuration of all tooltips in the
* application.
*
* var app = angular.module( 'App', ['ui.bootstrap.tooltip'], function( $tooltipProvider ) {
* // place tooltips left instead of top by default
* $tooltipProvider.options( { placement: 'left' } );
* });
*/
this.options = function(value) {
angular.extend(globalOptions, value);
};
/**
* This allows you to extend the set of trigger mappings available. E.g.:
*
* $tooltipProvider.setTriggers( 'openTrigger': 'closeTrigger' );
*/
this.setTriggers = function setTriggers(triggers) {
angular.extend(triggerMap, triggers);
};
/**
* This is a helper function for translating camel-case to snake-case.
*/
function snake_case(name) {
const regexp = /[A-Z]/g;
const separator = '-';
return name.replace(regexp, function(letter, pos) {
return (pos ? separator : '') + letter.toLowerCase();
});
}
/**
* Returns the actual instance of the $tooltip service.
* TODO support multiple triggers
*/
this.$get = [
'$window',
'$compile',
'$timeout',
'$document',
'$position',
'$interpolate',
function($window, $compile, $timeout, $document, $position, $interpolate) {
return function $tooltip(type, prefix, defaultTriggerShow) {
const options = angular.extend({}, defaultOptions, globalOptions);
/**
* Returns an object of show and hide triggers.
*
* If a trigger is supplied,
* it is used to show the tooltip; otherwise, it will use the `trigger`
* option passed to the `$tooltipProvider.options` method; else it will
* default to the trigger supplied to this directive factory.
*
* The hide trigger is based on the show trigger. If the `trigger` option
* was passed to the `$tooltipProvider.options` method, it will use the
* mapped trigger from `triggerMap` or the passed trigger if the map is
* undefined; otherwise, it uses the `triggerMap` value of the show
* trigger; else it will just use the show trigger.
*/
function getTriggers(trigger) {
const show = trigger || options.trigger || defaultTriggerShow;
const hide = triggerMap[show] || show;
return {
show: show,
hide: hide,
};
}
const directiveName = snake_case(type);
const startSym = $interpolate.startSymbol();
const endSym = $interpolate.endSymbol();
const template =
'<div ' +
directiveName +
'-popup ' +
'title="' +
startSym +
'title' +
endSym +
'" ' +
'content="' +
startSym +
'content' +
endSym +
'" ' +
'placement="' +
startSym +
'placement' +
endSym +
'" ' +
'animation="animation" ' +
'is-open="isOpen"' +
'>' +
'</div>';
return {
restrict: 'EA',
compile: function(tElem, tAttrs) {
const tooltipLinker = $compile(template);
return function link(scope, element, attrs) {
let tooltip;
let tooltipLinkedScope;
let transitionTimeout;
let popupTimeout;
let appendToBody = angular.isDefined(options.appendToBody)
? options.appendToBody
: false;
let triggers = getTriggers(undefined);
const hasEnableExp = angular.isDefined(attrs[prefix + 'Enable']);
let ttScope = scope.$new(true);
const positionTooltip = function() {
const ttPosition = $position.positionElements(
element,
tooltip,
ttScope.placement,
appendToBody
);
ttPosition.top += 'px';
ttPosition.left += 'px';
// Now set the calculated positioning.
tooltip.css(ttPosition);
};
// By default, the tooltip is not open.
// TODO add ability to start tooltip opened
ttScope.isOpen = false;
function toggleTooltipBind() {
if (!ttScope.isOpen) {
showTooltipBind();
} else {
hideTooltipBind();
}
}
// Show the tooltip with delay if specified, otherwise show it immediately
function showTooltipBind() {
if (hasEnableExp && !scope.$eval(attrs[prefix + 'Enable'])) {
return;
}
prepareTooltip();
if (ttScope.popupDelay) {
// Do nothing if the tooltip was already scheduled to pop-up.
// This happens if show is triggered multiple times before any hide is triggered.
if (!popupTimeout) {
popupTimeout = $timeout(show, ttScope.popupDelay, false);
popupTimeout
.then(reposition => reposition())
.catch(error => {
// if the timeout is canceled then the string `canceled` is thrown. To prevent
// this from triggering an 'unhandled promise rejection' in angular 1.5+ the
// $timeout service explicitly tells $q that the promise it generated is "handled"
// but that does not include down chain promises like the one created by calling
// `popupTimeout.then()`. Because of this we need to ignore the "canceled" string
// and only propagate real errors
if (error !== 'canceled') {
throw error;
}
});
}
} else {
show()();
}
}
function hideTooltipBind() {
scope.$evalAsync(function() {
hide();
});
}
// Show the tooltip popup element.
function show() {
popupTimeout = null;
// If there is a pending remove transition, we must cancel it, lest the
// tooltip be mysteriously removed.
if (transitionTimeout) {
$timeout.cancel(transitionTimeout);
transitionTimeout = null;
}
// Don't show empty tooltips.
if (!ttScope.content) {
return angular.noop;
}
createTooltip();
// Set the initial positioning.
tooltip.css({ top: 0, left: 0, display: 'block' });
ttScope.$digest();
positionTooltip();
// And show the tooltip.
ttScope.isOpen = true;
ttScope.$digest(); // digest required as $apply is not called
// Return positioning function as promise callback for correct
// positioning after draw.
return positionTooltip;
}
// Hide the tooltip popup element.
function hide() {
// First things first: we don't show it anymore.
ttScope.isOpen = false;
//if tooltip is going to be shown after delay, we must cancel this
$timeout.cancel(popupTimeout);
popupTimeout = null;
// And now we remove it from the DOM. However, if we have animation, we
// need to wait for it to expire beforehand.
// FIXME: this is a placeholder for a port of the transitions library.
if (ttScope.animation) {
if (!transitionTimeout) {
transitionTimeout = $timeout(removeTooltip, 500);
}
} else {
removeTooltip();
}
}
function createTooltip() {
// There can only be one tooltip element per directive shown at once.
if (tooltip) {
removeTooltip();
}
tooltipLinkedScope = ttScope.$new();
tooltip = tooltipLinker(tooltipLinkedScope, function(tooltip) {
if (appendToBody) {
$document.find('body').append(tooltip);
} else {
element.after(tooltip);
}
});
}
function removeTooltip() {
transitionTimeout = null;
if (tooltip) {
tooltip.remove();
tooltip = null;
}
if (tooltipLinkedScope) {
tooltipLinkedScope.$destroy();
tooltipLinkedScope = null;
}
}
function prepareTooltip() {
prepPlacement();
prepPopupDelay();
}
/**
* Observe the relevant attributes.
*/
attrs.$observe(type, function(val) {
ttScope.content = val;
if (!val && ttScope.isOpen) {
hide();
}
});
attrs.$observe(prefix + 'Title', function(val) {
ttScope.title = val;
});
function prepPlacement() {
const val = attrs[prefix + 'Placement'];
ttScope.placement = angular.isDefined(val) ? val : options.placement;
}
function prepPopupDelay() {
const val = attrs[prefix + 'PopupDelay'];
const delay = parseInt(val, 10);
ttScope.popupDelay = !isNaN(delay) ? delay : options.popupDelay;
}
const unregisterTriggers = function() {
element.unbind(triggers.show, showTooltipBind);
element.unbind(triggers.hide, hideTooltipBind);
};
function prepTriggers() {
const val = attrs[prefix + 'Trigger'];
unregisterTriggers();
triggers = getTriggers(val);
if (triggers.show === triggers.hide) {
element.bind(triggers.show, toggleTooltipBind);
} else {
element.bind(triggers.show, showTooltipBind);
element.bind(triggers.hide, hideTooltipBind);
}
}
prepTriggers();
const animation = scope.$eval(attrs[prefix + 'Animation']);
ttScope.animation = angular.isDefined(animation)
? !!animation
: options.animation;
const appendToBodyVal = scope.$eval(attrs[prefix + 'AppendToBody']);
appendToBody = angular.isDefined(appendToBodyVal)
? appendToBodyVal
: appendToBody;
// if a tooltip is attached to <body> we need to remove it on
// location change as its parent scope will probably not be destroyed
// by the change.
if (appendToBody) {
scope.$on(
'$locationChangeSuccess',
function closeTooltipOnLocationChangeSuccess() {
if (ttScope.isOpen) {
hide();
}
}
);
}
// Make sure tooltip is destroyed and removed.
scope.$on('$destroy', function onDestroyTooltip() {
$timeout.cancel(transitionTimeout);
$timeout.cancel(popupTimeout);
unregisterTriggers();
removeTooltip();
ttScope = null;
});
};
},
};
};
},
];
})
.directive('tooltip', [
'$tooltip',
function($tooltip) {
return $tooltip('tooltip', 'tooltip', 'mouseenter');
},
])
.directive('tooltipPopup', function() {
return {
restrict: 'EA',
replace: true,
scope: { content: '@', placement: '@', animation: '&', isOpen: '&' },
templateUrl: 'template/tooltip/tooltip-popup.html',
};
})
.directive('tooltipHtmlUnsafe', [
'$tooltip',
function($tooltip) {
return $tooltip('tooltipHtmlUnsafe', 'tooltip', 'mouseenter');
},
])
.directive('tooltipHtmlUnsafePopup', function() {
return {
restrict: 'EA',
replace: true,
scope: { content: '@', placement: '@', animation: '&', isOpen: '&' },
templateUrl: 'template/tooltip/tooltip-html-unsafe-popup.html',
};
});
}

View file

@ -24,6 +24,8 @@ export const plugin = (initializerContext: PluginInitializerContext) =>
new KibanaLegacyPlugin(initializerContext);
export * from './plugin';
export { initAngularBootstrap } from './angular_bootstrap';
export * from './angular';
export * from './notify';
export * from './utils';

View file

@ -4,7 +4,6 @@
* you may not use this file except in compliance with the Elastic License.
*/
import 'ui/angular-bootstrap';
import 'ace';
export { SavedObject, SavedObjectKibanaServices } from 'ui/saved_objects/types';

View file

@ -10,6 +10,7 @@ import { Plugin as DataPlugin } from 'src/plugins/data/public';
import { Storage } from '../../../../../src/plugins/kibana_utils/public';
import { LicensingPluginSetup } from '../../../../plugins/licensing/public';
import { NavigationPublicPluginStart as NavigationStart } from '../../../../../src/plugins/navigation/public';
import { initAngularBootstrap } from '../../../../../src/plugins/kibana_legacy/public';
export interface GraphPluginStartDependencies {
npData: ReturnType<DataPlugin['start']>;
@ -26,6 +27,7 @@ export class GraphPlugin implements Plugin {
private savedObjectsClient: SavedObjectsClientContract | null = null;
setup(core: CoreSetup, { licensing }: GraphPluginSetupDependencies) {
initAngularBootstrap();
core.application.register({
id: 'graph',
title: 'Graph',